CentOS 5.9
我有一个服务器,其中有一个 foo.ext.gz 文件位于各种随机目录中。
例子:
- /opt/fooapp/foosubdirectory/foo_randomnumber/blah/blah/foo.ext.gz
- /opt/fooapp/foosubdirectory/foo_ differentrandomnumber/blah/blah/foo.ext.gz
- /opt/fooapp/foosubdirectory/foo_another Differentrandomnumber/blah/blah/foo.ext.gz
我想运行一个 bash 命令来:
- 找到 foo.ext.gz 文件
- 提取gz文件的内容在其各自的 GZ 文件所在的同一目录中
- 保持原始 gz 文件完整。
如果我手动执行此操作,我会从find / -iname foo.ext.gz
.之后,我将复制文件所在的目录并输入如下内容:
gunzip -c /opt/fooapp/foosubdirectory/foo_12345/blah/blah/foo.ext.gz > /opt/fooapp/foosubdirectory/foo_12345/blah/blah/foo.ext
这里的问题是我需要手动对几十个文件/目录执行此过程。
有没有办法可以利用 xargs 或 for 循环?
答案1
虽然你可以解析 a 的输出,但find
你必须处理空格等。不幸的gunzip
是没有--keep
/-k
标志来保留(就像bzip2
和xz
一样)。
gunzipkeep
如果要制作一个带有一个参数(gzip 压缩文件)并进行解压缩的小脚本,请将该脚本放在 $PATH 中的某个位置并使用以下命令调用它:
find /opt/fooapp/foosubdirectory -name "foo.ext.gz" -print0 | xargs -0 --norun-if-empty --max-args 1 gunzipkeep
该脚本可能类似于:
#!/bin/bash
inname=$1
outname=${inname%.gz}
gunzip -c "$inname" > "$outname"
答案2
如果不使用辅助脚本(或 bash 函数),很难做到这一点,就像另一个答案中所做的那样,但并非不可能。这里使用-execdir
选项find
和一些bash
参数扩展。
find /opt/fooapp/foosubdirectory -name '*.gz' -execdir /bin/bash -c 'pwd ; echo ${0%.gz}; cp ${0} ${0%.gz}.tmp.gz ; gunzip ${0%.gz}.tmp.gz ; mv ${0%.gz}.tmp ${0%.gz}' {} \;
[编辑] 注意:您需要最新版本bash
(对于此特定参数扩展),某些旧版本没有此功能。我在 V 3.2.x 上测试了这个
[编辑] 注2:-execdir
据我所知,该表达式存在于 GNU find
(和其他现代实现)中,但不存在于较旧的实现中。我在 GNU find v 4.2.x 上测试了这个
为了可读性和评论进行了相同的重写:
find /opt/fooapp/foosubdirectory
-name '*.gz'
-execdir /bin/bash -c '_bash_command_string_ ' {} \;
# This ^ will run bash from the subdirectory containing the matched file
_bash_command_string_ -->
pwd ; # we are working in this subdir
echo ${0%.gz}; # this is matched filename (minus final .gz)
cp ${0} ${0%.gz}.tmp.gz ; # copy the .gz file as .tmp.gz
gunzip ${0%.gz}.tmp.gz ; # gunzip the .tmp.gz as .tmp
mv ${0%.gz}.tmp ${0%.gz} # rename .tmp as matched filename (minus final .gz)
作为一个聪明的解决方案,这个解决方案很有趣,但可能太复杂而无法在实践中使用。
看Bash 参考 - Shell 参数扩展, 搜索 ${parameter%word}
。
答案3
bash ≥4,运行shopt -s extglob
递归**/
遍历目录。 (请注意,这会遍历到目录的符号链接。在 zsh 中,您不需要任何特殊设置,并且**/
不会下降到符号链接,但***/
会下降。)然后一个简单的循环就足够了:
err=
for z in **/*.gz; do
gunzip <"$z" >"${z%.gz}" &&
touch -r "$z" "${z%.gz}" || # if you want to retain the file's modification time
err=1
done
if [ -n "$err" ]; then echo >&1 'Watch out, there were errors!'; fi
仅使用 POSIX sh
,从 调用 shell find
。提取错误状态更加困难 - 检查命令是否在 stderr 上产生任何内容。
find . -name '*.gz' -exec sh -c '
for z do gunzip <"$z" >"${z%.gz}" && touch -r "$z" "${z%.gz}"; done
' _ {} +
答案4
find . -name \*.gz | parallel gzip -dc {} \> {.}
{.}
是没有扩展名的输入线- 默认分隔符只是换行符
find . -name \*.gz | while read f; do gzip -dc "$f" > "${f%.gz}"; done
- 如果输入行可以以 IFS 中的字符开头或结尾或者是否可以包含反斜杠,则添加
IFS=
or-r