1. ホーム
  2. バッシュ

[解決済み】ファイルの各行に対してコマンドを実行する方法とは?

2022-04-09 08:54:42

質問

例えば、今、私は、Unixのパスをファイルに書き込んだいくつかのファイルを変更するために、次のように使っています。

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

もっとエレガントで安全な方法はないのでしょうか?

どのように解決するのですか?

ファイルを一行ずつ読んで、コマンドを実行する。4つの回答

答えは1つだけではないからです...。

  1. shell コマンドライン展開
  2. xargs 専用ツール
  3. while read 備考
  4. while read -u 専用 fd のために インタラクティブ 処理 (サンプル)

OPリクエストについて。 走行 chmod ファイルにリストされたすべてのターゲット上で , xargs は、指示されたツールです。しかし、他のアプリケーション、少量のファイルなどでは、....

0. ファイル全体をコマンドライン引数として読み込む。

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.

1. xargs は正しいツールです

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)

でテスト 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..

コマネチが行われる場所 1行に1回 .

2. while read とバリアントがあります。

OPにあるように cat file.txt | while read in; do chmod 755 "$in"; done はうまくいくのですが、2つの問題があります。

 - `cat |` is an *useless fork*, and

 - `| while ... ;done` will become a *subshell* where environment will disapear after `;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...

   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

  • を使用している間 STDIN 読み込み用 file.txt あなたのスクリプトは インタラクティブ (を使用することはできません。 STDIN を追加しました)。

3. while read -u を使用しています。 fd .

構文 while read ...;done <file.txt はリダイレクトされます。 STDIN から file.txt . つまり、処理が終了するまで、処理を行うことができないのです。

を作成する予定がある場合 インタラクティブ を使用しないようにしなければなりません。 STDIN を使用し、別の ファイルディスクリプタ .

定数 ファイルディスクリプタ があります。 0 に対して STDIN , 1 に対して STDOUT 2 に対して STDERR . で見ることができました。

    ls -l /dev/fd/

または

    ls -l /proc/self/fd/

そこから、未使用の番号を選択する必要があります。 063 (実際には、より多くの、依存する sysctl スーパーユーザツール)と同様に ファイルディスクリプタ :

このデモでは fd 7 :

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

そうすると read -u 7 このように

    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

閉じる fd/7 :

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

注意:私は ストライク この構文は、Parallels プロセスで多数の I/O を行う場合に便利だからです。

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