ファイルの行をコマンドの引数として使用するにはどうすればよいですか?


159

たとえば、引数をfoo.txt指定するファイルがあるN

arg1
arg2
...
argN

コマンドに渡す必要がある my_command

ファイルの行をコマンドの引数として使用するにはどうすればよいですか?


10
「引数」、複数、または「引数」、単一?受け入れられた回答は、本文が問い合わせる単一引数の場合のみ正しいですが、トピックが問い合わせるように見える複数引数の場合は正しくありません。
Charles Duffy

以下の4つの回答は通常同じ結果になりますが、セマンティクスは少し異なります。私はbashスクリプトの作成をやめた理由を覚えています:P
Navin

回答:


237

シェルが(特に)bashの場合、のショートカット$(cat afile)$(< afile)なので、次のように記述します。

mycommand "$(< file.txt)"

「コマンド置換」セクションのbashマニュアルページに記載されています。

または、コマンドをstdinから読み取って、次のようにします。 mycommand < file.txt


11
わかりやすくするために、<演算子を使用するのは簡単ではありません。これは、シェル自体がcatリダイレクトプロパティのバイナリを実行するのではなく、リダイレクトを実行することを意味します。
lstyls 2017年

2
これはカーネル設定なので、カーネルを再コンパイルする必要があります。または、xargsコマンドを調査する
glenn jackman 2017年

3
@ykanerがないため"$(…)"、の内容は、引数としてfile.txtmycommandはなく、の標準入力に渡されます。コマンドを実行し、出力を文字列として返す"$(…)"ことを意味します。ここで「コマンド」はファイルを読み取るだけですが、より複雑になる可能性があります。
törzsmókus

3
見積もりを失うまで、私にはうまくいきません。引用符を使用すると、ファイル全体が1つの引数として使用されます。引用符がないと、各行が個別の引数として解釈されます。
ピート

1
@LarsHあなたは絶対的に正しいです。おかげで、それはそれほど混乱しない方法です。
lstyls

37

すでに述べたように、バックティックまたはを使用できます$(cat filename)

言及されなかった、そして私が注意することが重要だと思うのは、シェルが空白によってファイルの内容を分解し、引数としてコマンドに見つけた各「単語」を与えることを覚えておかなければならないということです。また、空白やエスケープシーケンスなどを含めることができるようにコマンドライン引数を引用符で囲むこともできますが、ファイルからの読み取りは同じことを行いません。たとえば、ファイルに次のものが含まれているとします。

a "b c" d

取得する引数は次のとおりです。

a
"b
c"
d

各行を引数として取得する場合は、while / read / do構文を使用します。

while read i ; do command_name $i ; done < filename

私はあなたがbashを使用していることを前提としていると述べたはずです。他にもシェルがあることに気づきましたが、私が取り組んだほとんどすべての* nixマシンは、bashまたは同等のものを実行しました。IIRC、この構文はkshとzshで同じように機能するはずです。
ウィル

1
read -rバックスラッシュエスケープシーケンスを展開する場合を除いて、読み取りを行う必要があります。特に、NULは、改行よりも安全に使用できる区切り文字です。特に、渡す引数がファイル名のようなもので、リテラルの改行が含まれる場合があります。また、IFSをクリアしないと、先頭と末尾の空白がから暗黙的にクリアされiます。
Charles Duffy

18
command `< file`

stdinのコマンドにファイルの内容を渡しますが、改行を取り除きます。つまり、各行を個別に繰り返すことができませんでした。そのためには、「for」ループを使用してスクリプトを記述できます。

for line in `cat input_file`; do some_command "$line"; done

または(複数行バリアント):

for line in `cat input_file`
do
    some_command "$line"
done

または(の$()代わりに複数行のバリアント``):

for line in $(cat input_file)
do
    some_command "$line"
done

参照:

  1. ループ構文:https : //www.cyberciti.biz/faq/bash-for-loop/

また、< fileバックティックを配置すると、実際にはリダイレクトがまったく実行されませんcommand
Charles Duffy

3
(これを支持した人々は実際にそれをテストしましたか?bashでもPOSIX shでもcommand `< file` 動作ません。zsh は別の問題ですが、それはこの質問が扱っているシェルではありません)。
Charles Duffy 2017

@CharlesDuffyどのように機能しないのか、より具体的に説明できますか?
mwfearnley

@CharlesDuffyこれは、bash 3.2およびzsh 5.3では機能します。これはsh、ash、dashでは機能しませんが、$(<ファイル)では機能しません。
Arnie97

1
@mwfearnley、その特異性を提供させていただきます。目標は行を反復することですよね?入れてHello World1行に。最初に実行されsome_command Hello、次にsome_command World、またはではない ことがわかります。some_command "Hello World"some_command Hello World
Charles Duffy

15

あなたはバックティックを使用してそれを行います:

echo World > file.txt
echo Hello `cat file.txt`

4
これは引数を作成しません-引用符で囲まれていないため文字列分割の影響を受けるためecho "Hello * Starry * World" > file.txt、最初のステップで出力した場合、2つ目のコマンドに少なくとも4つの個別の引数が渡されます-おそらくさらに、*sは現在のディレクトリにあるファイルの名前に展開されます。
Charles Duffy

3
...そしてそれはグロブ拡張を実行しているので、ファイルの内容を正確に出力しません。また、コマンド置換操作を実行しているため、非常に非効率的です。実際fork()には、stdoutに接続されたFIFOを使用してサブシェルをオフにし、/bin/catそのサブシェルの子として呼び出し、FIFOを介して出力を読み取ります。と比較して$(<file.txt)、サブシェルやFIFOを使用せずにファイルの内容を直接bashに読み取ります。
チャールズダフィー2017

11

可能なすべてのコマンドライン引数(スペースを含む値、改行を含む値、リテラル引用符を含む値、印刷できない値、グロブ文字を含む値など)に対して機能する堅牢な方法でこれを行う場合は、少し時間がかかりますもっと面白い。


引数の配列を指定してファイルに書き込むには:

printf '%s\0' "${arguments[@]}" >file

...と交換し"argument one""argument two"必要に応じて、など。


そのファイルから読み取り、その内容を使用するには(bash、ksh93、または配列を持つ別の最近のシェル):

declare -a args=()
while IFS='' read -r -d '' item; do
  args+=( "$item" )
done <file
run_your_command "${args[@]}"

そのファイルから読み取り、その内容を使用するには(配列のないシェルで。これはローカルのコマンドライン引数リストを上書きするので、関数の引数を上書きするのではなく、関数の内部で行うのが最適です。グローバルリスト):

set --
while IFS='' read -r -d '' item; do
  set -- "$@" "$item"
done <file
run_your_command "$@"

-d(異なる行末デリミタの使用を許可する)は非POSIX拡張であり、配列のないシェルもそれをサポートしない場合があることに注意してください。その場合は、非シェル言語を使用して、NUL区切りのコンテンツをeval-safe形式に変換する必要がある場合があります。

quoted_list() {
  ## Works with either Python 2.x or 3.x
  python -c '
import sys, pipes, shlex
quote = pipes.quote if hasattr(pipes, "quote") else shlex.quote
print(" ".join([quote(s) for s in sys.stdin.read().split("\0")][:-1]))
  '
}

eval "set -- $(quoted_list <file)"
run_your_command "$@"

6

コマンドの引数としてファイルの内容を渡す方法は次のとおりです。

./foo --bar "$(cat ./bar.txt)"

1
これは(ただし、効率が大幅に低下するため)実質的に$(<bar.txt)(適切な拡張子を持つbashなどのシェルを使用する場合)と同等です。$(<foo)は特殊なケースです。で使用される通常のコマンド置換とは異なり、$(cat ...)操作するためにサブシェルをforkしないため、かなりのオーバーヘッドが回避されます。
Charles Duffy

3

あなたがする必要があるすべてはarguments.txt内容でファイルを回すことです

arg1
arg2
argN

my_command arg1 arg2 argNあなたは、単に使用することができますxargs

xargs -a arguments.txt my_command

xargs呼び出しに追加の静的引数を置くことができxargs -a arguments.txt my_command staticArgます。my_command staticArg arg1 arg2 argN


1

私のbashシェルでは、次のものが魅力のように機能しました:

cat input_file | xargs -I % sh -c 'command1 %; command2 %; command3 %;'

ここで、input_fileは

arg1
arg2
arg3

明らかなように、これにより、input_fileの各行で複数のコマンドを実行できます。これは、私がここで学んだ素晴らしいトリックです


3
あなたの「素敵な小さなトリック」はセキュリティの観点から危険です-を含む引数が$(somecommand)ある場合、そのコマンドはテキストとして渡されるのではなく実行されます。同様に、>/etc/passwdリダイレクトや上書き/etc/passwd(適切なアクセス許可で実行されている場合)などとして処理されます
Charles Duffy

2
代わりに(GNU拡張機能を備えたシステムで)以下を実行する方がはるかに安全です。- xargs -d $'\n' sh -c 'for arg; do command1 "$arg"; command2 "arg"; command3 "arg"; done' _入力ファイルの1行につき1つのシェルを開始するのではなく、可能な限り多くの引数を各シェルに渡すため、より効率的です。
チャールズダッフィー

そしてもちろん、無用な使用をcat
tripleee

0

@Wesley Riceの回答を2、3回編集した後、変更が大きくなりすぎて、自分で作成するのではなく、彼の回答を変更し続けることができないと判断しました。だから、私は自分で書く必要があると決めました!

ファイルの各行を読み取り、次のように1行ずつ操作します。

#!/bin/bash
input="/path/to/txt/file"
while IFS= read -r line
do
  echo "$line"
done < "$input"

これは、作成者のVivek Giteから直接提供されています:https : //www.cyberciti.biz/faq/unix-howto-read-line-by-line-from-file/。彼は信用を得ます!

構文:読むバッシュのUnixとLinuxのシェル上の行毎にファイル:
1. bashの、kshの、zshのため構文は次のとおりであり、すべての他のシェル行毎にファイルを読み込む
2. while read -r line; do COMMAND; done < input.file
3. -rオプションは、コマンドを読み取るために渡さバックスラッシュエスケープが解釈されないようにします。
4. IFS=読み取りコマンドの前にオプションを追加して、先頭/末尾の空白が削除されないようにします
-5。while IFS= read -r line; do COMMAND_on $line; done < input.file


そして今私が持っていたこの今閉じた質問に答えるために:ファイルからファイルのリストを `git add`することは可能ですか?-これが私の答えです:

FILES_STAGEDは、各行が私がやりたいファイルへの相対パスである一連の行を含むファイルへの絶対パスを含む変数であることに注意してくださいgit add。このコードスニペットは、このプロジェクトの「eRCaGuy_dotfiles / useful_scripts / sync_git_repo_to_build_machine.sh」ファイルの一部になり、開発中のファイルをあるPC(例:私がコーディングしたコンピューター)から別のPC(例:a私が構築するより強力なコンピューター):https : //github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles

while IFS= read -r line
do
    echo "  git add \"$line\""
    git add "$line" 
done < "$FILES_STAGED"

参照:

  1. 私が回答をコピーした場所: https //www.cyberciti.biz/faq/unix-howto-read-line-by-line-from-file/
  2. ループ構文:https : //www.cyberciti.biz/faq/bash-for-loop/

関連:

  1. ファイルの内容を行ごとに読み取って実行git addする方法:ファイルからファイルのリストを「git add」することはできますか?
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.