ls出力用のForeachループ

ls出力用のForeachループ

foreachループを使用してディレクトリ内のいくつかのファイルを処理してls出力を取得しようとしています。

IFS=$'\n'
for i in $(ls -1Atu); do
    echo "$i"
done

最初はうまくいくと思いましたが、*orと同じ名前のファイルを作成したときにfilename*シェルが*ワイルドカードとして解釈されたため、追加の繰り返しを追加しました。
ただし、このようにls出力をエスケープするとfor i in "$(ls -1Atu)";繰り返しが発生し、$ iはlsの出力を完全に含みます。
この問題をどのように解決できますか?より良いバリエーションを含むls -1Atu結果ファイルは、必要に応じて時間ごとにソートされます。ありがとう

答え1

背景素材:スペースやその他の特殊文字が原因でシェルスクリプトが停止するのはなぜですか?lsの出力を解析しない理由

改行に設定すると、IFSコマンド置換拡張中にスペースやタブではなく改行のみが区切り文字と見なされます。あなたの方法は改行文字を含むファイル名をサポートしません。これは1の基本的な制限ですls

また、これが問題です。この設定は、IFSコマンドが拡張機能をオーバーライドしたときに発生する他の作業(ワイルドカードの一致など)には影響しません。ワイルドカードをオフにすると、ファイル名に改行文字が含まれていない限り、スクリプトは機能します。

IFS='
'
set -- *"$IFS"*
if [ -e "$1" ]; then
  echo >&2 "There are file names with newlines. I cannot cope with this."
  exit 2
fi
set -f
for i in $(ls -1Atu); do
    printf '%s\n' "$i"
done
set +f
unset IFS

日付別にファイルをリストする簡単で信頼性の高い方法は、zshなどの純粋なBourneスタイルのシェル以外のものを使用することです。グローバル予選ワイルドカードの一致がソートされる方法を変更します。

#!/bin/zsh
files_by_access_time=(*(Doa))

1一部の実装ではこの問題を解決できますが、ls移植性が必要な場合はそうではありません。

答え2

空白の問題などに関連するさまざまな理由のためにお勧めできません。出力の解析ls。もう一つの方法はfind、、sortのGNUバージョンを使用することですsed

find . -mindepth 1 -maxdepth 1 -printf "%A@ %f\0" | 
  sort -rnz | 
  sed -z 's/^[0-9.]\+ //'
  • findもちろん、lsファイルをリストしてフィルタリングするよりもはるかに柔軟ですが、ソート自体を実行するわけではありません。
  • ここでは、修正時間を秒%A@単位で印刷し、その後にファイル名とファイル名を区別するために使用される唯一の安全な文字であるASCII NUL文字を印刷します。
  • sort -rnz次に、NULで区切られた入力(-z)を使用して-n数字を降順()でソートします(最新の項目が最初に提供されるため)-tls
  • その後、通常はsed初期時間を削除し、NULで区切られた行を再処理します(-z)。

生成された出力は、NULで区切られた行のストリームです。 Bash(通常shではない)では、次のように変数として読み取ることができます。

find . -mindepth 1 -maxdepth 1 -printf "%A@ %f\0" | 
  sort -rnz | 
  sed -z 's/^[0-9.]\+ //' |
  while IFS= read -r -d '' filename
  do 
    # do stuff with filenames
    printf "%s\n" "$filename"
  done

もちろん、これは少し複雑ですが、より安全です。

答え3

  1. *本当にファイル名を追加しますか?
  2. lsそれとも、実行権限がある場合はファイル名で終わる出力を提供するという意味ですか?*

出力に問題がある場合は、次のls方法で簡単に解決できます。

lsに置き換えられました\ls。これはエイリアシングされていないバージョンとして使用され、ls出力されません。*

答え4

次のように使用できます。

ls -1Atu | while IFS= read -r entry; do
    echo "$entry"
done

この例では、出力が一度生成され、そのwhile read entryセクションを介して出力がls1行ずつ解析されるため、例のforすべての項目が$i単一のラウンドに配置される問題が解決されます。

関連情報