回答:
不適切にls
出力を変数に入れてそれを実行echo
する代わりに、すべての色を削除する代わりに、
ls -a1
から man ls
-1 list one file per line. Avoid '\n' with -q or -b
ls
を表示する以外は、の出力については何もしないことをお勧めします:)
たとえば、シェルグロブとfor
ループを使用して、ファイルで何かを行います...
shopt -s dotglob # to include hidden files*
for i in R/*; do echo "$i"; done
* ただし、これには現在のディレクトリ.
またはその親は含まれません..
echo "$i"
と printf "%s\n" "$i"
(1ファイル名が始まる場合窒息しないことを「 - 」)。実際、ほとんどの場合echo something
はに置き換える必要がありprintf "%s\n" "something"
ます。
R
ここから始まりますが、一般的にはそうです。しかし、スクリプトを作成する場合でも、常に先行-
するパスに対応しようとすると問題が発生します。人々は--
、一部のコマンドのみがサポートするがオプションの終わりを意味すると想定します。がサポートしていないfind . [tests] -exec [cmd] -- {} \;
場所を確認[cmd]
しました--
。そこにあるすべての道は.
とにかく始まります!しかし、それは交換に対しては、引数ませんecho
とprintf '%s\n'
。ここでは、ループ全体をに置き換えることができます printf '%s\n' R/*
。Bashはprintf
組み込みとして持っているので、引数の数/長さに事実上制限はありません。
@muruが提案したように引用符で囲むと、実際に要求したとおりに機能しますが、このために配列を使用することも検討してください。例えば:
IFS=$'\n' dirs=( $(find . -type d) )
IFS=$'\n'
唯一の配列の各要素を取得改行characcters Oの出力を分割するためにはbashに指示します。これがないと、スペースで分割さa file name with spaces.txt
れるため、1つの要素ではなく5つの別々の要素になります。\n
ただし、ファイル/ディレクトリ名に改行()を含めることができる場合、このアプローチは機能しません。コマンドの出力の各行を配列の要素として保存します。
古いスタイル`command`
を$(command)
優先構文に変更したことに注意してください。
これで、という配列があり$dirs
、各bofの要素は、前のコマンドの出力の行です。例えば:
$ find . -type d
.
./olad
./ho
./ha
./ads
./bar
./ga
./da
$ IFS=$'\n' dirs=( $(find . -type d) )
$ for d in "${dirs[@]}"; do
echo "DIR: $d"
done
DIR: .
DIR: ./olad
DIR: ./ho
DIR: ./ha
DIR: ./ads
DIR: ./bar
DIR: ./ga
DIR: ./da
ここで、bashの奇妙さ(ここを参照)のIFS
ために、これを実行した後、元の値にリセットする必要があります。それで、それを保存して再署名します:
oldIFS="$IFS"
IFS=$'\n' dirs=( $(find . -type d) )
IFS="$oldIFS"
または手動で行う:
IFS=" "$'\t\n '
または、現在のターミナルを閉じるだけです。新しいものには、元のIFSが再び設定されます。
du
OPの見栄えを良くするための部分は、出力形式を保持することにもっと関心があります。
mapfile
それを簡単にします。まず、コマンドの出力をまったく保存する必要があるかどうかを検討します。そうでない場合は、コマンドを実行してください。
コマンドの出力を行の配列として読み取る場合は、グロビングを無効にし、IFS
行で分割するように設定し、(
)
配列作成構文内でコマンド置換を使用し、IFS
後でリセットするのが1つの方法であることは事実です。これはより複雑であるそれはそうより。terdonの答えは、そのアプローチの一部をカバーしています。ただし、代わりに、テキストを行の配列として読み取るためmapfile
のBashシェルの組み込みコマンドであるを使用することをお勧めします。次に、ファイルから読み取る単純なケースを示します。
mapfile < filename
これは、行をと呼ばれる配列に読み込みますMAPFILE
。入力のリダイレクトが なければ、で指定されたファイルの代わりに、シェルの標準入力(通常は端末)から読み取ることになります。デフォルト以外の配列に読み込むには、その名前を渡します。たとえば、これは配列に読み込みます:< filename
filename
MAPFILE
lines
mapfile lines < filename
変更を選択できる別のデフォルトの動作は、行末の改行文字がそのまま残されることです。それらは各配列要素の最後の文字として表示されます(ただし、入力が改行文字なしで終了した場合を除きます。この場合、最後の要素には改行文字がありません)。これらの改行を短くして配列に表示されないようにするには、-t
オプションをに渡しますmapfile
。たとえば、これは配列records
を読み取り、末尾の改行文字を配列要素に書き込みません。
mapfile -t records < filename
-t
配列名を渡さずに使用することもできます。つまり、の暗黙の配列名でMAPFILE
も機能します。
mapfile
シェル組み込みは、他のオプションをサポートしており、それが、代わりとして呼び出すことができますreadarray
。詳細についてはhelp mapfile
、実行(およびhelp readarray
)してください。
ただし、ファイルから読み取るのではなく、コマンドの出力から読み取る必要があります。そのためには、プロセス置換を使用します。このコマンドは、コマンドから行を読み込むsome-command
とarguments...
、それらにそのコマンドライン引数や場所などmapfile
のデフォルトのアレイMAPFILE
その終端の改行文字では、削除しました:
mapfile -t < <(some-command arguments...)
プロセスの置換は、runningの出力を読み取ることができる実際のファイル名に置き換えられます。ファイルは通常のファイルではなく名前付きパイプであり、Ubuntuでは(場合によっては以外の番号が付けられる)という名前になりますが、シェルが処理するため、詳細を気にする必要はありません。すべての舞台裏。<(some-command arguments...)
some-command arguments...
/dev/fd/63
63
代わりにを使用してプロセスの置換を省略できると思うかもしれませんが、で区切られた複数のコマンドのパイプラインがある場合、Bashランはすべてのコマンドをサブシェルで実行するため、機能しません。したがって、両方のと、自分の中で実行する環境から初期化していますが、パイプラインを実行したシェルの環境から分離しています。が実行されるサブシェルでは、配列にデータが入力されますが、コマンドが終了するとその配列は破棄されます。呼び出し元に対して作成または変更されることはありません。some-command arguments... | mapfile -t
|
some-command arguments...
mapfile -t
mapfile -t
MAPFILE
MAPFILE
terdonの回答の例は、次のようになりますmapfile
。
mapfile -t dirs < <(find . -type d)
for d in "${dirs[@]}"; do
echo "DIR: $d"
done
それでおしまい。が設定されているかどうかを確認し、IFS
設定されているかどうかとその値を追跡し、改行に設定して、後でリセットまたは再設定解除する必要はありません。あなたは無効にする必要はありませんグロブ(例えば、でset -f
) -あなたはファイル名が含まれているかもしれないので、真剣にそのメソッドを使用するようにしている場合は、本当に必要とされている*
、?
と[
--then(それを再度有効set +f
後で)。
また、特定のループを1つのprintf
コマンドで完全に置き換えることもできます。ただし、これは実際にはの利点ではありません。配列を作成するために、または別の方法をmapfile
使用するかどうかに関係なく実行できますmapfile
。ここに短いバージョンがあります:
mapfile -t < <(find . -type d)
printf 'DIR: %s\n' "${MAPFILE[@]}"
terdonが述べているように、行ごとの操作は常に適切であるとは限らず、改行を含むファイル名では正しく機能しないことに留意することが重要です。そのようにファイルに名前を付けることはお勧めしませんが、偶発的にも発生する可能性があります。
「すべてのシナリオに対応する一般的なコマンド」と、mapfile
いくらか使用するアプローチがその目標に近づくように求めましたが、要件を再検討することをお勧めします。
上記のタスクは、1つのfind
コマンドだけで実現できます。
find . -type d -printf 'DIR: %p\n'
各行の先頭sed
に追加DIR:
するような外部コマンドを使用することもできます。これは間違いなくやや醜く、そのfindコマンドとは異なり、改行を含むファイル名内に追加の「プレフィックス」が追加されますが、入力に関係なく機能するため、要件を満たしているため、出力を変数または配列:
find . -type d | sed 's/^/DIR: /'
リストを作成し、見つかった各ディレクトリに対してアクションを実行する必要がある場合(some-command
ディレクトリのパスを実行して引数として渡すなど)も、find
それを行うことができます。
find . -type d -print -exec some-command {} \;
別の例として、各行に接頭辞を追加する一般的なタスクに戻りましょう。出力を表示したいhelp mapfile
が、行に番号を付けたいとしましょう。私は実際にmapfile
はこれを使用せず、それをシェル変数またはシェル配列に読み込む他のメソッドも使用しません。help mapfile | cat -n
私が望むフォーマットを与えないと仮定すると、私は使うことができますawk
:
help mapfile | awk '{ printf "%3d: %s\n", NR, $0 }'
コマンドのすべての出力を単一の変数または配列に読み込むことは有用で適切な場合がありますが、大きな欠点があります。既存のコマンドまたは既存のコマンドの組み合わせが既に必要以上のことを既に実行している可能性がある場合に、シェルを使用するという複雑さに対処する必要があるだけでなく、コマンドの出力全体をメモリに保存する必要があります。それが問題ではないことを知っている場合もあれば、大きなファイルを処理している場合もあります。
一般的に試みられ、時には正しく行われる代替策read -r
は、ループで入力を1行ずつ読み取ることです。後の行を操作するときに前の行を保存する必要がなく、長い入力を使用する必要がある場合は、よりも優れている場合がありますmapfile
。ただし、ほとんどの場合、作業を実行できるコマンドにパイプで接続できる場合は、これも回避する必要があります。