小编典典

您如何为文件的每一行运行命令,例如 chmod?

all

例如,现在我正在使用以下内容来更改我将其 Unix 路径写入文件的几个文件:

cat file.txt | while read in; do chmod 755 "$in"; done

有没有更优雅、更安全的方式?


阅读 77

收藏
2022-06-08

共1个答案

小编典典

逐行读取文件并执行命令:4 个答案

这是因为不仅有1个答案…

  1. Shell 命令行扩展
  2. xargs专用工具
  3. while read附上一些评论
  4. while read -u使用专用 fd , 进行 交互 处理(示例)

关于 OP 请求: _ 在 file 中列出的所有目标上 _ 运行chmodxargs是指示的工具。但是对于其他一些应用程序,少量文件等......

0. 读取整个文件作为命令行参数。

如果您的文件不是太大并且所有文件都 命名良好 (没有空格或其他特殊字符,如引号),您可以使用 shell 命令行扩展 。简单地:

chmod 755 $(<file.txt)

对于少量文件(行),此命令是较轻的命令。

1.xargs是正确的工具

对于更大数量的文件,或输入文件中几乎 任意 数量的行…

对于许多 binutils 工具,如chown, chmod, rm, cp -t

xargs chmod 755 <file.txt

如果您在file.txt.

xargs -0 chmod 755 < <(tr \\n \\0 <file.txt)

如果您的命令需要为每个条目准确运行 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)

seq 1 5作为输入进行测试

尝试这个:

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.while read和变体。

正如OP建议的那样,

cat file.txt |
while read in; do
    chmod 755 "$in"
done

会工作,但有两个问题:

  • cat |是一个 无用的叉子 ,并且

  • | while ... ;done将成为一个 子shell ,其环境将在之后消失;done

所以这可以更好地写:

while read in; do
    chmod 755 "$in"
done < file.txt

  • 您可能会收到以下警告$IFSread标志:

help read

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

当您使用来自标准输入for readingfile.txt` 的重定向时,您的脚本无法以交互方式读取其他输入(您不能再将标准输入用于其他输入)。

3. while read、使用专用 fd

语法:while read ...;done <file.txt将标准输入重定向到来自file.txt. 这意味着您将无法处理流程,直到它们完成。

这将让您同时使用多个输入,您可以合并两个文件(如这里:scriptReplay.sh),或者也许:

您计划创建一个 交互式 工具,您必须避免使用标准输入并使用一些替代文件描述符。

常量文件描述符是:

  • 0 标准输入
  • 1个标准输出
  • 2 为标准误。

3.1 [posix ](http://localhost:8080/questions/tagged/posix

“显示标记为“posix”的问题”) shell优先

您可以通过以下方式查看它们:

ls -l /dev/fd/

或者

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下相同](http://localhost:8080/questions/tagged/bash

“显示标记为“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

或者

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/
2022-06-08