
引用された回答は引き続き表示されます。このリンク明確に述べる「解析しないでくださいls
!」これが私を悩ませる理由は次のとおりです。
そのリンクの情報は何の問題もなく大量に受け入れられるようですが、軽く読みながら少なくともいくつかのエラーを見つけることができます。
そのリンクに記載されている問題も解決策を探しているという欲求を引き起こさないようです。
最初の段落から:
...
[ls]
ファイルのリストを要求すると大きな問題があります。 Unix では、空白、改行、カンマ、パイプ記号、ファイル名として使用したいほとんどすべての文字を含む、ファイル名にほぼすべての文字を使用できます。 NUL 以外の区切り文字です。 ...ls
改行でファイル名を区切ります。ファイル名に改行文字が含まれるまでは問題ありません。ls
改行文字の代わりにNUL文字でファイル名を終了できる実装がわからないためls
。
本当に迷惑なことですか?どのように一度改行を含む可能性があるデータに対して、改行で終わるリストされたデータセットを処理できますか?さて、このサイトの質問に答える人が毎日これをやらなかったら、おそらく私たちは問題に陥ったと思います。
しかし、実際には、ほとんどの実装はls
出力を解析するための非常に単純なAPIを提供し、私たちはそれを認識することなく常にこれを行います。ファイル名をnullで終わることができるだけでなく、nullまたは必要な任意の文字列で始めることもできます。さらに、これらのランダムな文字列を割り当てることもできます。ファイルタイプ別。以下を考慮してください。
LS_COLORS='lc=\0:rc=:ec=\0\0\0:fi=:di=:' ls -l --color=always | cat -A
total 4$
drwxr-xr-x 1 mikeserv mikeserv 0 Jul 10 01:05 ^@^@^@^@dir^@^@^@/$
-rw-r--r-- 1 mikeserv mikeserv 4 Jul 10 02:18 ^@file1^@^@^@$
-rw-r--r-- 1 mikeserv mikeserv 0 Jul 10 01:08 ^@file2^@^@^@$
-rw-r--r-- 1 mikeserv mikeserv 0 Jul 10 02:27 ^@new$
line$
file^@^@^@$
^@
バラよりこれもっと学ぶ。
今私が本当に興味を持っているのは、この記事の次の部分です。
$ ls -l
total 8
-rw-r----- 1 lhunath lhunath 19 Mar 27 10:47 a
-rw-r----- 1 lhunath lhunath 0 Mar 27 10:47 a?newline
-rw-r----- 1 lhunath lhunath 0 Mar 27 10:47 a space
問題は、の出力で
ls
ユーザーまたはコンピュータのどの部分がファイル名を構成しているかわからないことです。それはすべての単語ですか?いいえ、一行ずつですか?いいえ。この質問には正解はありません。ただ、あなたが知らないという点を除けばね。また
ls
、時にはファイル名データが壊れることがあります(この場合、\n
単語の間に文字が入ります)。「ㅏ」そして 「新しいチーム」になる?疑問符......
現在のディレクトリ内のすべてのファイルを繰り返すには、
for
ループとglobを使用してください。
for f in *; do
[[ -e $f ]] || continue
...
done
作家はそう呼ぶ無効なファイル名ls
シェルグローバル変数を含むファイル名のリストを返す場合それからファイルリストを検索するには、Shell globを使用することをお勧めします!
以下を考慮してください。
printf 'touch ./"%b"\n' "file\nname" "f i l e n a m e" |
. /dev/stdin
ls -1q
f i l e n a m e
file?name
IFS="
" ; printf "'%s'\n" $(ls -1q)
'f i l e n a m e'
'file
name'
POSIXの定義そしてオペランドは次のように-1
なります-q
ls
。
-q
- 印刷できないファイル名文字と<tab>
sのすべてのインスタンスを疑問符('?'
)文字で強制的に作成します。実装は、出力が端末装置で構成されている場合、デフォルトでこのオプションを提供できます(MAY)。
-1
-(最初。)1行に1つの項目として出力するようにします。
ワイルドカードには固有の問題があります?
。どの文字なので、?
リスト内の複数の一致は同じファイルと複数回一致します。これは扱いやすいです。
これを行う方法は重要ではありませんが、最終的に行うことはあまりありません。以下に説明します。私が興味を持っているものは何ですか?なぜできないの。私の考えでは、この質問に対する最良の答えは受け入れられることです。人々に彼らが知っていることを知らせることに集中することをお勧めします。できる彼らがすることよりすることできません。少なくともあなたが間違っていることが証明される可能性ははるかに少ないと思います。
しかし、なぜ試してみますか?もちろん、私の主な動機は、他の人が私にできないと言ったことでした。ls
何を見つけるべきかを知る限り、結果は必要に応じて規則的で予測可能であることが非常に明らかです。エラーメッセージはほとんどのものよりも私を迷惑にさせます。
しかし、問題はPatrickとWumpus Qを除いてそうです。ウォームリーの答え(後者は素晴らしいハンドルを持っていますが)私はここにある答えのほとんどの情報がほとんど正しいと思います。シェルglobは、現在のディレクトリを検索するときに解析するよりも使いやすく、一般的に効率的ですls
。しかし、少なくとも私の考えでは、上記の記事で引用された誤った情報を広めることを正当化するのに十分ではなく、「受け取ることができる理由」でもありません。解析されていませんls
。」
zsh
Patrickの答えで一貫性のない結果は、主にthenを使用した結果ですbash
。 - デフォルトでは - 単語分割コマンドの結果はzsh
移植可能な方法で置き換えられません。だから彼が尋ねたとき$(
)
残りのファイルはどこに行きましたか?この質問に対する答えはあなたの殻はそれらを食べます。これが、移植可能なシェルコードで作業するときにこの変数を設定する必要がある理由ですSH_WORD_SPLIT
。zsh
私は彼の答えでこれを言及していないことは非常に誤解を招くと思います。
Wumpusの答えは私には適していませんでした。リストコンテキストの?
役割はいシェルボール。何を言うべきかわかりません。
複数の結果を処理するには、globの欲を制限する必要があります。以下は、ひどいファイル名のテストライブラリを作成して表示します。
{ printf %b $(printf \\%04o `seq 0 127`) |
sed "/[^[-b]*/s///g
s/\(.\)\(.\)/touch '?\v\2' '\1\t\2' '\1\n\2'\n/g" |
. /dev/stdin
echo '`ls` ?QUOTED `-m` COMMA,SEP'
ls -qm
echo ; echo 'NOW LITERAL - COMMA,SEP'
ls -m | cat
( set -- * ; printf "\nFILE COUNT: %s\n" $# )
}
出力
`ls` ?QUOTED `-m` COMMA,SEP
??\, ??^, ??`, ??b, [?\, [?\, ]?^, ]?^, _?`, _?`, a?b, a?b
NOW LITERAL - COMMA,SEP
?
\, ?
^, ?
`, ?
b, [ \, [
\, ] ^, ]
^, _ `, _
`, a b, a
b
FILE COUNT: 12
/slash
それでは、または英数字ではなくシェルグローブのすべての文字を保護-dash
し、一意の結果リストを保護します。印刷できないすべての文字が保存されているため安全です。より::colon
sort -u
ls
for f in $(
ls -1q |
sed 's|[^-:/[:alnum:]]|[!-\\:[:alnum:]]|g' |
sort -u | {
echo 'PRE-GLOB:' >&2
tee /dev/fd/2
printf '\nPOST-GLOB:\n' >&2
}
) ; do
printf "FILE #$((i=i+1)): '%s'\n" "$f"
done
出力:
PRE-GLOB:
[!-\:[:alnum:]][!-\:[:alnum:]][!-\:[:alnum:]]
[!-\:[:alnum:]][!-\:[:alnum:]]b
a[!-\:[:alnum:]]b
POST-GLOB:
FILE #1: '?
\'
FILE #2: '?
^'
FILE #3: '?
`'
FILE #4: '[ \'
FILE #5: '[
\'
FILE #6: '] ^'
FILE #7: ']
^'
FILE #8: '_ `'
FILE #9: '_
`'
FILE #10: '?
b'
FILE #11: 'a b'
FILE #12: 'a
b'
以下では、この問題に再びアクセスしますが、別のアプローチを使用します。\0
NULLを除いて、/
ASCII文字はパス名で禁止されている唯一のバイトであることを覚えておいてください。私はglobを残して、代わりにPOSIX指定-d
オプションls
とPOSIX指定-exec $cmd {} +
構文を組み合わせましたfind
。find
自然に 1 つだけが順番にエクスポートされるため、/
以下を使用すると、各項目のすべてのディレクトリ項目情報を含む、繰り返し、安定して区切られたファイルのリストを簡単に取得できます。次のように何ができるかを想像してください。
#v#note: to do this fully portably substitute an actual newline \#v#
#v#for 'n' for the first sed invocation#v#
cd ..
find ././ -exec ls -1ldin {} + |
sed -e '\| *\./\./|{s||\n.///|;i///' -e \} |
sed 'N;s|\(\n\)///|///\1|;$s|$|///|;P;D'
###OUTPUT
152398 drwxr-xr-x 1 1000 1000 72 Jun 24 14:49
.///testls///
152399 -rw-r--r-- 1 1000 1000 0 Jun 24 14:49
.///testls/?
\///
152402 -rw-r--r-- 1 1000 1000 0 Jun 24 14:49
.///testls/?
^///
152405 -rw-r--r-- 1 1000 1000 0 Jun 24 14:49
.///testls/?
`///
...
ls -i
非常に便利です。特に結果の一意性が疑われる場合にはそうです。
ls -1iq |
sed '/ .*/s///;s/^/-inum /;$!s/$/ -o /' |
tr -d '\n' |
xargs find
これらは私が考えることができる最も携帯性に優れた手段です。 GNUを使用すると、ls
次のことができます。
ls --quoting-style=WORD
最後に、より簡単な方法があります。分析するls
私はinode番号が必要なときにこれをよく使用します:
ls -1iq | grep -o '^ *[0-9]*'
これは、別の便利なPOSIX関連オプションであるinode番号のみを返します。
答え1
私はこれをまったく信じていません。しかし、議論のためにあなたが仮定しましょうできる、十分な努力を傾ける準備ができていれば、ls
「敵対者」(あなたが書いたコードを知って意図的にコードを破損するように設計されたファイル名を選択した人)に対しても出力を安定的に解析できます。
こうすることはできても、これはまだ良い考えではありません。。
Bourne Shell 1はひどい言語です。極端な移植性が他の要因よりも重要でない限り、複雑なものには使用しないでくださいautoconf
。
ls
解析された出力がシェルスクリプトへの抵抗が最も少ないパスであるように見える問題がある場合、これは現在実行中の操作が次のようになるという強力な表示です。シェルスクリプトが複雑すぎるPerl、Python、Julia、または他の言語でコンテンツ全体を書き換える必要があります。いいね使いやすいスクリプト言語。デモとして、これはPythonで書いた最後のプログラムです:
import os, sys
for subdir, dirs, files in os.walk("."):
for f in dirs + files:
ino = os.lstat(os.path.join(subdir, f)).st_ino
sys.stdout.write("%d %s %s\n" % (ino, subdir, f))
ファイル名に異常な文字があっても問題ありません。出力は、出力がls
あいまいではないようにあいまいではありません。しかし、これは結果が直接使用される「実際の」プログラム(これらのデモとは対照的に)では重要ではありませんos.path.join(subdir, f)
。
また、作成したものとは異なり、これから6ヶ月経ってもまだ意味があり、少し異なる操作を実行する必要があるときに簡単に修正できることも重要です。たとえば、ドットファイルとエディタのバックアップを除いて、すべてを基本名に基づいてアルファベット順に処理する必要があるとします。
import os, sys
filelist = []
for subdir, dirs, files in os.walk("."):
for f in dirs + files:
if f[0] == '.' or f[-1] == '~': continue
lstat = os.lstat(os.path.join(subdir, f))
filelist.append((f, subdir, lstat.st_ino))
filelist.sort(key = lambda x: x[0])
for f, subdir, ino in filelist:
sys.stdout.write("%d %s %s\n" % (ino, subdir, f))
1はい、Bourne シェルの拡張バージョンは今すぐ使いやすく、bash
すべてzsh
元のバージョンよりはるかに優れています。コア「シェルユーティリティ」(find、grepなど)のGNU拡張も多くの役に立ちます。しかし、すべての拡張にもかかわらず、シェル環境は改善されません十分実際に良いスクリプト言語と競合するには、どのシェルについて話していても、「複雑な操作を実行するためにシェルを使用しないでください」というアドバイスが残ります。
「良いスクリプト言語でも良いインタラクティブシェルはどのようなものですか?」は、インタラクティブCLIに必要な利便性(例:代わりに入力を許可する)とcc -c -g -O2 -o foo.o foo.c
複雑subprocess.run(["cc", "-c", "-g", "-O2", "-o", "foo.o", "foo.c"])
なスクリプトエラーの微妙さ(例:いいえ任意の位置にある引用符でない単語を文字列リテラルとして解釈します。このようなものをデザインしようとすると、おそらくIPython、PowerShell、Luaを最初にBlenderに投げます。しかし、結果がどのように出てくるのか分からない。
答え2
このリンクは、情報が完全に正確であり、長い間使用されていたので、何度も参照されています。
ls
印刷できない文字をグローバル文字に置き換える例ですが、その文字は実際のファイル名にありません。これはなぜ重要ですか? 2つの理由:
- そのファイル名をプログラムに渡すと、ファイル名は実際には存在しません。実際のファイル名を取得するには、グローブを拡張する必要があります。
- ファイルglobは複数のファイルと一致することができます。
たとえば、
$ touch a$'\t'b
$ touch a$'\n'b
$ ls -1
a?b
a?b
まったく同じように見える2つのファイルがあることに注意してください。両方とマークされている場合、どのように区別できますかa?b
?
lsがシェルグローブを含むファイル名のリストを返すとき、作成者はそれを間違ったファイル名として参照し、シェルグローブを使用してファイルリストを検索することをお勧めします。
ここに違いがあります。図に示すように、globを返すと、globが複数のファイルと一致する可能性があります。ただし、globと一致する結果を繰り返すと、globではなく正確なファイルが返されます。
たとえば、
$ for file in *; do printf '%s' "$file" | xxd; done
0000000: 6109 62 a.b
0000000: 610a 62 a.b
出力にではなく元の文字が含まれているように見えることをxxd
確認してください。$file
\t
\n
?
を使用すると、ls
次のような結果が得られます。
for file in $(ls -1q); do printf '%s' "$file" | xxd; done
0000000: 613f 62 a?b
0000000: 613f 62 a?b
「とにかく繰り返すのに、なぜ使用しないのですかls
?」
あなたが与えた例は実際には動作しません。うまくいくようですが、そうではありません。
私はこれについて言及しています:
for f in $(ls -1q | tr " " "?") ; do [ -f "$f" ] && echo "./$f" ; done
私は複数のファイル名でディレクトリを作成しました。
$ for file in *; do printf '%s' "$file" | xxd; done
0000000: 6120 62 a b
0000000: 6120 2062 a b
0000000: 61e2 8082 62 a...b
0000000: 61e2 8083 62 a...b
0000000: 6109 62 a.b
0000000: 610a 62 a.b
コードを実行すると、次のような結果が表示されます。
$ for f in $(ls -1q | tr " " "?") ; do [ -f "$f" ] && echo "./$f" ; done
./a b
./a b
残りのファイルはどこに行きましたか?
これを試してみましょう:
$ for f in $(ls -1q | tr " " "?") ; do stat --format='%n' "./$f"; done
stat: cannot stat ‘./a?b’: No such file or directory
stat: cannot stat ‘./a??b’: No such file or directory
./a b
./a b
stat: cannot stat ‘./a?b’: No such file or directory
stat: cannot stat ‘./a?b’: No such file or directory
それでは、実際の地球儀を試してみましょう。
$ for f in *; do stat --format='%n' "./$f"; done
./a b
./a b
./a b
./a b
./a b
./a
b
バッシュと
上記の例では、通常のシェルzshを使用しています。 Bashを使用してプロセスを繰り返すと、例とはまったく異なる別の結果セットが表示されます。
同じファイルセット:
$ for file in *; do printf '%s' "$file" | xxd; done
0000000: 6120 62 a b
0000000: 6120 2062 a b
0000000: 61e2 8082 62 a...b
0000000: 61e2 8083 62 a...b
0000000: 6109 62 a.b
0000000: 610a 62 a.b
コードと全く異なる結果:
for f in $(ls -1q | tr " " "?") ; do stat --format='%n' "./$f"; done
./a b
./a b
./a b
./a b
./a
b
./a b
./a b
./a b
./a b
./a b
./a b
./a b
./a
b
./a b
./a b
./a b
./a b
./a
b
Shell globを使用すると、非常にうまく機能します。
$ for f in *; do stat --format='%n' "./$f"; done
./a b
./a b
./a b
./a b
./a b
./a
b
bashがこのように動作する理由は、私が答えの冒頭で言及した「ファイルglobが複数のファイルと一致する可能性がある」という点の1つにさかのぼります。
ls
複数のファイルに対して同じ glob( a?b
) を返すので、この glob が拡張されるたびに一致するすべてのファイルを取得します。
使用中のファイルのリストを再生成するにはどうすればよいですか?
touch 'a b' 'a b' a$'\xe2\x80\x82'b a$'\xe2\x80\x83'b a$'\t'b a$'\n'b
16進コードはUTF-8 NBSP文字です。
答え3
の出力はls -q
まったく球形ではありません。?
「ここに直接表示できない文字があります」という意味です。 Globは?
「ここで許可されているすべての文字」を意味しました。
Globsには別の特殊文字があります(*
少なくともこのペアにはより多くの特殊[]
文字[]
があります)。これらのどれも脱出できませんでしたls -q
。
$ touch x '[x]'
$ ls -1q
[x]
x
出力をグローブセットとして処理して拡張すると、2回だけでls -1q
なく完全に見逃すことがあります。 globとしては文字列自体と一致しません。x
[x]
ls -q
これは、奇妙なキャラクターから目や端末を保護するためのものであり、シェルに戻すことができるものを作成するためのものではありません。
答え4
答えは簡単です。ls
処理する必要がある特別な状況は、可能な利点よりも大きいです。ls
出力を解析しないと、これらの特殊なケースを回避できます。
ここにあるマントラは次のとおりです。ユーザーファイルシステムを決して信頼しないでください(同等ユーザー入力を絶対に信頼しないでください。)。常に100%確実に動作する方法がある場合でも、ls
同じように動作しますが、確実性は落ちても好む方法でなければなりません。技術的な詳細はすでに取り上げているので、扱いません。テデンそしてパトリック広く。私は重要な(おそらく高価な)取引で私の仕事/評判を使う危険があるので、避けることができればls
不確実性のない解決策を好むことを知っています。
一部の人はもっと好きだと知っていますリスクは確実性よりも重要です、しかしバグレポートを提出しました。。