你如何为文件的每一行运行一个命令? [英] How do you run a command for each line of a file?

查看:19
本文介绍了你如何为文件的每一行运行一个命令?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

cat file.txt |读入时;做 chmod 755 "$in";完毕

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

解决方案

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

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

  1. shell 命令行扩展
  2. xargs 专用工具
  3. while read 一些备注
  4. while read -u 使用专用的fd,用于交互式处理(示例)

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

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

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

    chmod 755 $(

    对于少量的文件(行),这个命令是较轻的.

  2. xargs 是正确的工具

    对于更大数量的文件,或者输入文件中几乎任何行数...

    对于许多binutils 工具,例如chownchmodrmcp -t ...

    xargs chmod 755 

    如果您在 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)废话1废话1..废话2废话2..废话3废话3..废话4废话4..废话 5 废话 5..

    在命令处每行一次.

  3. while read 和变体.

    正如 OP 所建议的 cat file.txt |读入时;做 chmod 755 "$in";done 会起作用,但有两个问题:

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

    • <代码>|而 ... ;done 将成为一个 subshel​​l,其中环境将在 ;done 后消失.

    所以这可以写得更好:

    读入时;做 chmod 755 "$in";完成<文件.txt

    但是,

    • 您可能会收到有关 $IFSread 标志的警告:

      帮助阅读

      <块引用>

      read: read [-r] ... [-d delim] ... [name ...]...从标准输入中读取一行...该行被拆分与分词一样进入字段,并分配第一个词到第一个 NAME,第二个词到第二个 NAME,依此类推...只有在 $IFS 中找到的字符才能被识别为单词分隔符....选项:...-d delim 继续直到读到 DELIM 的第一个字符,而不是换行...-r 不允许反斜杠转义任何字符...退出状态:返回码为零,除非遇到文件结束...

      在某些情况下,您可能需要使用

      while IFS= read -r in;do chmod 755 "$in";done 

      为了避免奇怪的文件名问题.也许如果您遇到 UTF-8 的问题:

      while LANG=C IFS= read -r in ;do chmod 755 "$in";done 

    • 当您使用 STDIN 读取 file.txt 时,您的脚本不能交互式(您不能使用 不再是标准输入).

  4. while read -u,使用专用的fd.

    语法:while read ...;done 将重定向 STDINfile.txt.这意味着,在流程完成之前,您将无法处理流程.

    如果你打算创建交互式工具,你必须避免使用STDIN并使用一些替代的文件描述符em>.

    常量文件描述符是:0表示STDIN1表示STDOUT2 用于 STDERR.您可以通过以下方式查看它们:

    ls -l/dev/fd/

    ls -l/proc/self/fd/

    从那里,您必须在 063 之间选择未使用的数字(更多,实际上取决于 sysctl 超级用户工具)作为文件描述符:

    对于这个演示,我将使用 fd 7:

    exec 7<file.txt # `7` 和 `<` 之间没有空格!ls -l/dev/fd/

    那么你可以这样使用read -u 7:

    while read -u 7 filename;doans=;while [ -z "$ans" ];do读取 -p "进程文件 '$filename' (y/n)?" -sn1 foo[$foo"]&&[ -z "${foo/[yn]}" ]&&ans=$foo ||回声'??'完毕如果 [ "$ans" = "y" ] ;那么回声 是echo "正在处理 '$filename'."别的回声 否菲完成 7

    完成

    关闭fd/7:

    exec 7<&- # 这将关闭文件描述符 7.ls -l/dev/fd/

    注意:我使用 striked 版本,因为这种语法在使用并行进程执行许多 I/O 时很有用:

    mkfifo sshfifo执行7>>(ssh -t user@host sh >sshfifo)exec 6

For example, right now I'm using the following to change a couple of files whose Unix paths I wrote to a file:

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

Is there a more elegant, safer way?

解决方案

Read a file line by line and execute commands: 4 answers

This is because there is not only 1 answer...

  1. shell command line expansion
  2. xargs dedicated tool
  3. while read with some remarks
  4. while read -u using dedicated fd, for interactive processing (sample)

Regarding the OP request: running chmod on all targets listed in file, xargs is the indicated tool. But for some other applications, small amount of files, etc...

  1. Read entire file as command line argument.

    If your file is not too big and all files are well named (without spaces or other special chars like quotes), you could use shell command line expansion. Simply:

    chmod 755 $(<file.txt)
    

    For small amount of files (lines), this command is the lighter one.

  2. xargs is the right tool

    For bigger amount of files, or almost any number of lines in your input file...

    For many binutils tools, like chown, chmod, rm, cp -t ...

    xargs chmod 755 <file.txt
    

    If you have special chars and/or a lot of lines in file.txt.

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

    if your command need to be run exactly 1 time by entry:

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

    This is not needed for this sample, as chmod accept multiple files as argument, but this match the title of question.

    For some special case, you could even define location of file argument in commands generateds by xargs:

    xargs -0 -I '{}' -n 1 myWrapper -arg1 -file='{}' wrapCmd < <(tr \n \0 <file.txt)
    

    Test with seq 1 5 as input

    Try this:

    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..
    

    Where commande is done once per line.

  3. while read and variants.

    As OP suggest cat file.txt | while read in; do chmod 755 "$in"; done will work, but there is 2 issues:

    • cat | is an useless fork, and

    • | while ... ;done will become a subshell where environment will disapear after ;done.

    So this could be better written:

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

    But,

    • You may be warned about $IFS and read flags:

      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...
      

      In some case, you may need to use

      while IFS= read -r in;do chmod 755 "$in";done <file.txt
      

      For avoiding problems with stranges filenames. And maybe if you encouter problems with UTF-8:

      while LANG=C IFS= read -r in ; do chmod 755 "$in";done <file.txt
      

    • While you use STDIN for reading file.txt, your script could not be interactive (you cannot use STDIN anymore).

  4. while read -u, using dedicated fd.

    Syntax: while read ...;done <file.txt will redirect STDIN to file.txt. That mean, you won't be able to deal with process, until they finish.

    If you plan to create interactive tool, you have to avoid use of STDIN and use some alternative file descriptor.

    Constants file descriptors are: 0 for STDIN, 1 for STDOUT and 2 for STDERR. You could see them by:

    ls -l /dev/fd/
    

    or

    ls -l /proc/self/fd/
    

    From there, you have to choose unused number, between 0 and 63 (more, in fact, depending on sysctl superuser tool) as file descriptor:

    For this demo, I will use fd 7:

    exec 7<file.txt      # Without spaces between `7` and `<`!
    ls -l /dev/fd/
    

    Then you could use read -u 7 this way:

    while read -u 7 filename;do
        ans=;while [ -z "$ans" ];do
            read -p "Process file '$filename' (y/n)? " -sn1 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
    

    done
    

    To close fd/7:

    exec 7<&-            # This will close file descriptor 7.
    ls -l /dev/fd/
    

    Nota: I let striked version because this syntax could be usefull, when doing many I/O with parallels process:

    mkfifo sshfifo
    exec 7> >(ssh -t user@host sh >sshfifo)
    exec 6<sshfifo
    

这篇关于你如何为文件的每一行运行一个命令?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆