シェルスクリプトのディレクトリにあるファイルのリストを取得するにはどうすればよいですか?


159

シェルスクリプトを使用してディレクトリの内容を取得しようとしています。

私のスクリプトは:

for entry in `ls $search_dir`; do
    echo $entry
done

ここで、$search_dir相対パスです。ただし、$search_dir名前に空白を含む多くのファイルが含まれています。その場合、このスクリプトは期待どおりに実行されません。

を使用できることはわかっていますがfor entry in *、これは現在のディレクトリでのみ機能します。

そのディレクトリに移動し、使用しfor entry in *てから元に戻すことができることはわかっていますが、私の特定の状況では、それを行うことができません。

私には2つの相対パス$search_dirとがあり$work_dir、両方で同時に作業し、それらを読み取り、ファイルの作成/削除などを行う必要があります。

だから私は今何をしますか?

PS:私はbashを使用しています。

回答:


274
for entry in "$search_dir"/*
do
  echo "$entry"
done

4
なぜ機能for entry in "$search_dir/*"しないのか説明できますか?なぜ/*引用符の外に置く必要があるのですか?
mrgloom 2016

6
@mrgloom:シェルにワイルドカードをグロブさせる必要があるため。
Ignacio Vazquez-Abrams

ではないでしょうfind速くなりますか?
Alexej Magura 2017年

4
ソリューションは完全なパスを提供します。現在のディレクトリにあるものをリストしたい場合はどうなりますか?
ThatsRightJack 2017

1
@mrgloomあなたがそうしたいなら、それでそれを達成することができますfor entry in "${search_dir}/*"
funilrys

28

ここでの他の答えは素晴らしいです、そしてあなたの質問に答えてください、しかしこれは「bash get list of files in files in directory」(私がファイルのリストを保存するために探していた)のトップのgoogleの結果なので、私は投稿すると思いましたその問題への答え:

ls $search_path > filename.txt

特定のタイプ(たとえば、.txtファイル)のみが必要な場合:

ls $search_path | grep *.txt > filename.txt

$ search_pathはオプションです。ls> filename.txtは現在のディレクトリを実行します。


.txtファイルのみを取得するためにgrepを使用する必要はありません: `ls $ search_path / *。txt> filename.txt '。しかし、より重要なのは、lsコマンドの出力を使用してファイル名を解析しないことです。
Victor Zamanian 2017年

1
@VictorZamanian、なぜlsファイル名を解析するためにの出力を使用してはいけないのか詳しく説明できますか?これについてこれまで聞いたことがない。
samurai_jane

1
@samurai_janeこのトピックに関して提供するリンクはたくさんありますが、最初の検索結果はmywiki.wooledge.org/ParsingLsです。lsの出力を解析しない理由はBSであり、それについて非常に詳細であると主張するSOに関する質問を見たことさえありました。しかし、回答/回答はそれでも悪い考えだと主張しました。見て:unix.stackexchange.com/questions/128985/...
ビクターZamanian

26

これは、構文を理解しやすくするための方法です。

yourfilenames=`ls ./*.txt`
for eachfile in $yourfilenames
do
   echo $eachfile
done

./は現在の作業ディレクトリですが、任意のパスに置き換えることができ、
*.txtanything.txt を返します。ターミナルにコマンドを直接
入力することで、何が一覧表示されるかを簡単に確認できますls

基本的に、yourfilenameslistコマンドが個別の要素として返すすべてを含む変数を作成し、それをループします。ループは、ループする変数eachfileの1つの要素(この場合はファイル名)を含む一時変数を作成します。これは必ずしも他の回答よりも優れているとは限りませんが、lsコマンドとforループの構文に既に精通しているため、直感的に理解できます。


これは迅速で非公式のスクリプトやワンライナーでは問題なく機能しますが、グロブベースのソリューションとは異なり、ファイル名に改行が含まれていると機能しなくなります。
Soren Bjornstad

@SorenBjornstadアドバイスありがとう!ファイル名に改行が許可されていることを知りませんでした。どのようなファイルに改行が含まれるのでしょうか?これはよくあることですか?
rrr

ファイル名の改行はこの理由から悪であり、私が知る限り、それらを使用する正当な理由はありません。自分で野生で見たことはありません。とはいえ、改行を使って悪意を持ってファイル名を作成し、これを悪用することは完全に可能です。(たとえば、ファイルを含むディレクトリを想像ABC。あなたが呼ばれるファイルを作成B\nCし、Dこの権利を処理しないそれらを削除することを選択した後に、本ソフトウェアは、あなたが持っていなかった場合でも、代わりに既存のファイルBとCを削除してしまう可能性がありそれを行う許可。)
Soren Bjornstad

19
for entry in "$search_dir"/* "$work_dir"/*
do
  if [ -f "$entry" ];then
    echo "$entry"
  fi
done

11
find "${search_dir}" "${work_dir}" -mindepth 1 -maxdepth 1 -type f -print0 | xargs -0 -I {} echo "{}"

1
これはかなり古いですが、最後のxargs -0 -i echo "{}"コマンドを取得できていないようです。少し説明してください。特にその-i echo "{}"部分は何をしているのですか?また、現在非推奨となっmanているページを読んで、insted -iを使用する必要があります-I
drkg4b 2015年

1
-i{}argで置き換えます。
Noel Yap、2015年

ありがとう!これは、私のような気の遅い人にも役立ちます。これは{}findコマンドによって一致に置き換えられる文字列だと思います。
drkg4b 2015年

3
なぜ使うのxargs?デフォルトでは、find検出した内容を出力します...からすべてを削除できます-print0
gniourf_gniourf

1
これを行うと、スペースを含むファイルエントリが適切に処理されません。
Noel Yap

4
$ pwd; ls -l
/home/victoria/test
total 12
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:31  a
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:31  b
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:31  c
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:32 'c d'
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:31  d
drwxr-xr-x 2 victoria victoria 4096 Apr 23 11:32  dir_a
drwxr-xr-x 2 victoria victoria 4096 Apr 23 11:32  dir_b
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:32 'e; f'

$ find . -type f
./c
./b
./a
./d
./c d
./e; f

$ find . -type f | sed 's/^\.\///g' | sort
a
b
c
c d
d
e; f

$ find . -type f | sed 's/^\.\///g' | sort > tmp

$ cat tmp
a
b
c
c d
d
e; f

バリエーション

$ pwd
/home/victoria

$ find $(pwd) -maxdepth 1 -type f -not -path '*/\.*' | sort
/home/victoria/new
/home/victoria/new1
/home/victoria/new2
/home/victoria/new3
/home/victoria/new3.md
/home/victoria/new.md
/home/victoria/package.json
/home/victoria/Untitled Document 1
/home/victoria/Untitled Document 2

$ find . -maxdepth 1 -type f -not -path '*/\.*' | sed 's/^\.\///g' | sort
new
new1
new2
new3
new3.md
new.md
package.json
Untitled Document 1
Untitled Document 2

ノート:

  • . :現在のフォルダー
  • 削除-maxdepth 1して再帰的に検索
  • -type f:ディレクトリではなくファイルを検索(d
  • -not -path '*/\.*' :戻らない .hidden_files
  • sed 's/^\.\///g':先頭に追加./したものを結果リストから削除します

0

ディレクトリ内のファイルを一覧表示する別の方法を次に示します(別のツールを使用して、他の回答の一部ほど効率的ではありません)。

cd "search_dir"
for [ z in `echo *` ]; do
    echo "$z"
done

echo *現在のディレクトリのすべてのファイルを出力します。forループはstdoutに各ファイル名とプリントを反復処理します。

さらに、ディレクトリ内でディレクトリを探す場合は、これをforループ内に配置します。

if [ test -d $z ]; then
    echo "$z is a directory"
fi

test -d ファイルがディレクトリかどうかを確認します。


0

受け入れられた回答は、接頭辞が。のファイルを返しません。それを行うには

for entry in "$search_dir"/* "$search_dir"/.[!.]* "$search_dir"/..?*
do
  echo "$entry"
done

-1

Linuxバージョンでは、(x86_64 GNU / Linux)で次の作業を行います。

for entry in "$search_dir"/*
do
  echo "$entry"
done

-1:変数を引用しないことを除いてこれは受け入れられた回答と同じです。引用符$search_dirを付けないということは、ディレクトリ名にスペースが含まれていると壊れるということです。(この場合$entry、引用符を付けなくてもかまいませんが、引用符を
付けることをお勧め

あなたは間違っている。私の解決策は、受け入れられた答えに似ていても同じではありません。そしてそれは間違いではありません、それは機能します。Mac OSを含むさまざまなUNIXシステムで試してみましたが、すべてのシステムで動作しました。名前に先行ブランクがある場合でも、ブランクのあるファイルは正しく表示されます。search_dir =。$ search_dir / *のエントリ用; $ entryをエコーし​​ます。done ./ aa aaa.txt ./add ./apm_tables.txtソリューションが機能しない、または誤った結果が生成されることを証明できる場合は、ソリューションを投票することができます。
Andrushenko Alexander

私のコメントをもう少し詳しく読むと、問題はファイル名ではなくディレクトリ名に空白が含まれていることがわかります。これは、質問者が遭遇した問題とは異なりますが、それでもコードが意図せず壊れることがあります!機能しない例を次に示しますmkdir "x y"; touch "x y/1.txt"; search_dir="x y"; for entry in $search_dir/*; do echo $entry; done。ループは2回実行されます。1回は印刷x、2回目は印刷されますがy/*、これはおそらく期待される結果ではありません。
Soren Bjornstad

そして、不適切に引用符で囲まれていない変数は危険なほど間違っていると考えます。これは、反対票の根拠です。ファイルを残しながら、検索ディレクトリのサブディレクトリを削除するには、次のコードを検討してくださいsearch_dir="x y"; for entry in $search_dir/*; do if [ -d "$entry" ]; then rm -rf "$entry"; fi; donex現在のディレクトリにディレクトリも存在する場合、変数を引用していないために、そのディレクトリはすぐに削除されます!
Soren Bjornstad

まあ、あなたは常に1つのタスク用に作成されたコードを取り、タスクを少し変更するとコードが間違ってしまいます。質問のために私の提案された解決策は機能しました。しかし...開発者である私はあなたの主張に同意する必要があります。コードをより堅牢で一般的なものにすることができるなら、そうすべきです。だから私はqoutesを追加します。これにより、私の回答は上記の回答と同じになりますが、ディスカッション(誰かにとって役立つかもしれません)のために、回答も残します。
Andrushenko Alexander

-1

次の簡単なコマンドを入力するだけです。

ls -d */

@Michael Sisko
Vipul

ls(リスト)-d(ディレクトリのみをリスト)* /(任意のパターンで、バックスラッシュはリストをディレクトリのみに制限します)。
Michael Sisko
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.