例如,现在我正在使用以下命令来更改几个文件,这些文件的Unix路径是我写到一个文件中的:
cat file.txt | while read in; do chmod 755 "$in"; done
有没有更优雅、更安全的方式?
例如,现在我正在使用以下命令来更改几个文件,这些文件的Unix路径是我写到一个文件中的:
cat file.txt | while read in; do chmod 755 "$in"; done
有没有更优雅、更安全的方式?
Yes.
while read in; do chmod 755 "$in"; done < file.txt
这样可以避免cat过程。
对于这种目的,猫几乎总是不好的。你可以阅读更多关于无用的猫。
如果你有一个很好的选择器(例如所有的。txt文件都在一个目录下) 你可以这样做:
for i in *.txt; do chmod 755 "$i"; done
Bash for循环
或者是你的变种:
while read line; do chmod 755 "$line"; done < file.txt
如果你知道你在输入中没有任何空白:
xargs chmod 755 < file.txt
如果路径中可能有空格,如果你有GNU xargs:
tr '\n' '\0' < file.txt | xargs -0 chmod 755
我看到你标记了bash,但Perl也是一种很好的方式:
perl -p -e '`chmod 755 $_`' file.txt
你也可以应用一个regex来确保你得到了正确的文件,例如只处理。txt文件:
perl -p -e 'if(/\.txt$/) `chmod 755 $_`' file.txt
要“预览”正在发生的事情,只需将反撇号替换为双引号并预打印:
perl -p -e 'if(/\.txt$/) print "chmod 755 $_"' file.txt
逐行读取文件并执行命令:4+答案
因为shell(和/或bash)的主要用途是运行其他命令,所以答案不止一个!!
0。Shell命令行扩展 1。Xargs专用工具 2。边读边加备注 3所示。当读取-u时使用专用fd,用于交互处理(示例) 5。使用内联生成的脚本运行shell
对于OP请求:在文件中列出的所有目标上运行chmod, xargs是指定的工具。但对于其他一些应用程序,少量的文件,等等…
0. 读取整个文件作为命令行参数。
如果您的文件不是太大,并且所有文件都有良好的命名(没有空格或其他特殊字符,如引号),您可以使用shell命令行展开。简单:
chmod 755 $(<file.txt)
对于少量的文件(行),这个命令是较轻的一个。
1. Xargs是合适的工具
对于较大数量的文件,或者输入文件中几乎任何数量的行…
对于许多binutils工具,如chown, chmod, rm, cp -t…
xargs chmod 755 <file.txt
可以在find文件的管道后面使用:
find /some/path -type f -uid 1234 -print | xargs chmod 755
如果你在file.txt中有特殊字符和/或很多行。
xargs -0 chmod 755 < <(tr \\n \\0 <file.txt)
find /some/path -type f -uid 1234 -print0 | xargs -0 chmod 755
如果您的命令需要为每个条目运行1次:
xargs -0 -n 1 chmod 755 < <(tr \\n \\0 <file.txt)
本例不需要这样做,因为chmod接受多个文件作为参数,但这与问题的标题相匹配。
对于一些特殊的情况,你甚至可以在xargs生成的命令中定义文件参数的位置:
xargs -0 -I '{}' -n 1 myWrapper -arg1 -file='{}' wrapCmd < <(tr \\n \\0 <file.txt)
用seq15作为输入进行测试
试试这个:
xargs -n 1 -I{} echo Blah {} blabla {}.. < <(seq 1 5)
Blah 1 blabla 1..
Blah 2 blabla 2..
Blah 3 blabla 3..
Blah 4 blabla 4..
Blah 5 blabla 5..
命令每行执行一次。
2. 而读和变式。
正如OP所言,
cat file.txt |
while read in; do
chmod 755 "$in"
done
会起作用,但有两个问题:
猫|是个没用的叉子,还有 |而……done将成为一个子shell,其环境将在;done之后消失。
所以这样写更好:
while read in; do
chmod 755 "$in"
done < file.txt
But
你可能会被警告$IFS和阅读标志:
帮助读
read: read [-r] ... [-d delim] ... [name ...] ... Reads a single line from the standard input... The line is split into fields as with word splitting, and the first word is assigned to the first NAME, the second word to the second NAME, and so on... Only the characters found in $IFS are recognized as word delimiters. ... Options: ... -d delim continue until the first character of DELIM is read, rather than newline ... -r do not allow backslashes to escape any characters ... Exit Status: The return code is zero, unless end-of-file is encountered...
在某些情况下,您可能需要使用
while IFS= read -r in;do
chmod 755 "$in"
done <file.txt
用来避免奇怪文件名的问题。如果你遇到UTF-8的问题:
while LANG=C IFS= read -r in ; do
chmod 755 "$in"
done <file.txt
当您使用从标准输入重定向来读取file.txt '时,您的脚本不能交互式地读取其他输入(您不能再使用标准输入来读取其他输入)。
3.读取时,使用专用的fd。
while read…;done <file.txt将标准输入重定向为来自file.txt。这意味着您将无法处理流程,直到它们完成。
这将允许你同时使用多个输入,你可以合并两个文件(像这里:scriptReplay.sh),或者:
您计划创建一个交互式工具,您必须避免使用标准输入,并使用一些替代的文件描述符。
常量文件描述符是:
0为标准输入 1为标准输出 2为标准误差。
3.1 posix shell优先
你可以通过以下途径看到它们:
ls -l /dev/fd/
or
ls -l /proc/$$/fd/
从这里,您必须在0到63之间选择未使用的数字(实际上,更多的数字取决于sysctl超级用户工具)作为您的文件描述符。
对于这个演示,我将使用文件描述符7:
while read <&7 filename; do
ans=
while [ -z "$ans" ]; do
read -p "Process file '$filename' (y/n)? " foo
[ "$foo" ] && [ -z "${foo#[yn]}" ] && ans=$foo || echo '??'
done
if [ "$ans" = "y" ]; then
echo Yes
echo "Processing '$filename'."
else
echo No
fi
done 7<file.txt
如果你想在更多不同的步骤中读取输入文件,你必须使用:
exec 7<file.txt # Without spaces between `7` and `<`!
# ls -l /dev/fd/
read <&7 headLine
while read <&7 filename; do
case "$filename" in
*'----' ) break ;; # break loop when line end with four dashes.
esac
....
done
read <&7 lastLine
exec 7<&- # This will close file descriptor 7.
# ls -l /dev/fd/
3.2 bash下相同
在bash下,你可以让他为你选择任何空闲的fd并存储到一个变量中:exec {varname}</path/to/input:
while read -ru ${fle} filename;do
ans=
while [ -z "$ans" ]; do
read -rp "Process file '$filename' (y/n)? " -sn 1 foo
[ "$foo" ] && [ -z "${foo/[yn]}" ] && ans=$foo || echo '??'
done
if [ "$ans" = "y" ]; then
echo Yes
echo "Processing '$filename'."
else
echo No
fi
done {fle}<file.txt
Or
exec {fle}<file.txt
# ls -l /dev/fd/
read -ru ${fle} headline
while read -ru ${fle} filename;do
[[ -n "$filename" ]] && [[ -z ${filename//*----} ]] && break
....
done
read -ru ${fle} lastLine
exec {fle}<&-
# ls -l /dev/fd/
5. 过滤用于创建shell命令的输入文件
sed <file.txt 's/.*/chmod 755 "&"/' | sh
这不会优化fork,但对于更复杂(或条件)的操作可能有用:
sed <file.txt 's/.*/if [ -e "&" ];then chmod 755 "&";fi/' | sh
sed 's/.*/[ -f "&" ] \&\& echo "Processing: \\"&\\"" \&\& chmod 755 "&"/' \
file.txt | sh
如果您想为每一行并行运行命令,可以使用GNU parallel
parallel -a <your file> <program>
文件的每一行都将作为参数传递给程序。默认情况下,并行运行与cpu数量相同的线程。但是你可以用-j来指定它
您还可以使用AWK,它可以为您提供更大的灵活性来处理文件
awk '{ print "chmod 755 "$0"" | "/bin/sh"}' file.txt
如果你的文件有一个字段分隔符:
字段 1,字段 2,字段 3
只得到第一个字段
awk -F, '{ print "chmod 755 "$1"" | "/bin/sh"}' file.txt
你可以在GNU文档中查看更多细节 https://www.gnu.org/software/gawk/manual/html_node/Very-Simple.html#Very-Simple
这一逻辑也适用于许多其他目标。 如何从/home/文件系统中读取每个用户的.sh_history ?如果有上千个呢?
#!/bin/ksh
last |head -10|awk '{print $1}'|
while IFS= read -r line
do
su - "$line" -c 'tail .sh_history'
done
下面是脚本https://github.com/imvieira/SysAdmin_DevOps_Scripts/blob/master/get_and_run.sh
现在(在GNU Linux中)xargs仍然是这个问题的答案,但是…你现在可以使用-a选项直接从文件中读取输入:
xargs -a file.txt -n 1 -I {} chmod 775 {}