
この記事では、Bashプログラマーによるエラーについて説明します。 上記の例にはすべていくつかの欠陥があります。 常に引用符を使用し、単語分割を使用しない場合は、以下で説明するエラーの多くを回避できます! ワードブレイキングは、Bourneシェルから継承された欠陥のあるレガシープラクティスです。 展開を引用符で囲まない場合、デフォルトで使用されます。 一般に、落とし穴の大部分は引用符なしの置換に何らかの形で関係しており、単語の分割と結果のグロビングにつながります。
内容
- for i in $(ls * .mp3)
- cp $ file $ターゲット
- 先行するダッシュファイル名
- [$ foo = "bar"]
- cd $(ディレクトリ名「$ f」)
- ["$ foo" = bar && "$ bar" = foo]
- [[$ foo> 7]]
- grep foo bar | 読み取り中-r; do((count ++)); やった
- if [grep foo myfile]
- if [bar = "$ foo"]; その後...
- if [[a = b] && [c = d]]; その後...
- fooを読む
- catファイル| sed s / foo / bar />ファイル
- echo $ foo
- $ foo = bar
- foo = bar
- エコー<< EOF
- su -c 'いくつかのコマンド'
- cd / foo; バー
- [バー== "$ foo"]
- {1..10}のi do ./something&; やった
- cmd1 && cmd2 || cmd3
- echo "Hello World!"
- arg for $ *
- 関数foo()
- エコー「〜」
- local varname = $(コマンド)
- export foo =〜/ bar
- sed 's / $ foo /さようなら/'
- tr [AZ] [az]
- ps ax | grep gedit
- printf "$ foo"
- {1 .. $ n}のi
- if [[$ foo = $ bar]](目的に応じて)
- if [[$ foo =〜 'some RE']]
- [-n $ foo]または[-z $ foo]
- [[-e "$ broken_symlink"]]は、$ broken_symlinkの存在にもかかわらず1を返します
- Edファイルがクラッシュ<<< "g / d \ {0.3 \} / s // e / g"
- サブストリングexprが一致に失敗しました
- UTF-8およびバイトオーダーマーク、BOMについて
- 内容= $(<ファイル)
- ./*のファイルの場合。 if [[$ file!= *。*]]
- somecmd 2>&1 >>ログファイル
- cmd; ((!$?))|| 死ぬ
- y = $((配列[$ x]))
- numを読み取ります。 エコー$((num + 1))
- IFS =、読み取り-raフィールド<<< "$ csv_line"
- export CDPATH =。:〜/ myProject
1. $のi(ls * .mp3)
BASHプログラマーが犯す最も一般的な間違いの1つ。 そのようなサイクルを書くことで表現されます:
for i in $(ls *.mp3); do # ! some command $i # ! done for i in $(ls) # ! for i in `ls` # ! for i in $(find . -type f) # ! for i in `find . -type f` # ! files=($(find . -type f)) # ! for i in ${files[@]} # !
はい、
ls
の出力を処理したり、ファイル名のリストとして
find
して反復したり
find
と便利です。 しかし、 できません 。 このアプローチは完全に誤りであり、修正する方法はありません。 これに完全に異なるアプローチをする必要があります。
ここには少なくとも5つの問題があります。
- ファイル名にスペースが含まれている場合、 WordSplittingの対象となります。 現在のフォルダに
01 - Don't Eat the Yellow Snow.mp3
という名前のファイルがあるとします。for
ループは各単語を反復処理し、01、-、Do n't、Eatなどの結果を生成します。
- ファイル名にグロブ文字が含まれている場合、グロビング(「 グロビング 」)が行われます。 出力
ls
に文字*が含まれている場合、それが属する単語はテンプレートと見なされ、それに対応するすべてのファイル名のリストに置き換えられます。 ファイルパスには、NUL以外の任意の文字を含めることができます。 はい、改行を含む。
-
ls
ユーティリティはファイル名を破損する可能性があります。 作業するプラットフォーム、使用する(または使用しない)引数、および標準出力が端末を示しているかどうかによって、ls
はファイル名の一部の文字を突然「?」に置き換える場合があります。 または、まったく表示しないようにします。 ls出力を解析しようとしないでください 。
- CommandSubstitutionは、出力からすべての末尾の改行を切り捨てます。 一見すると、
ls
は新しい行を追加するため、これは良いことです。 しかし、リストの最後のファイル名が新しい行で終わる場合、 `...`または$()
は削除され、追加されます。
また、二重引用符を囲むことはできません。
for i in "$(ls *.mp3)"; do # !
これにより、出力
ls
全体が1つの単語と見なされます。 各ファイル名を反復処理する代わりに、ループが1回実行され、結合されたファイル名から文字列値を
i
に割り当てます。 また、 IFSを新しい行に変更することはできません。 ファイル名には改行も含まれる場合があります。
このトピックのもう1つのバリエーションは、ワードブレークの誤用と、ファイルの行を(誤って)読み取るforループです。 例:
IFS=$'\n' for line in $(cat file); do ... # !
これは機能しません! 特に文字列がファイル名の場合。 Bash(Bourneファミリーの他のシェルと同様)は、そのようには機能しません。 上記のすべてに加えて、
ls
自体を使用する必要はまったくありません。 これは外部コマンドであり、その出力は人間が読むために特別に設計されており、スクリプトの解析用ではありません。
それで、あなたはどうやってそれを正しくしますか?
たとえば、
find
を
-exec
と組み合わせて使用し
find
。
find . -type f -exec some command {} \;
ls
代わりに
ls
このオプションを検討できます。
for i in *.mp3; do # ! ... some command "$i" # ... ! done
BashのようなPOSIXシェルには、特にこのためのグロビングプロパティがあります。これにより、マップされたファイル名のリストにテンプレートを適用できます。 外部ユーティリティの結果を解釈する必要はありません。 グロビングは置換手順の最後のステップであるため、引用符なしの置換の影響を受けない個々の単語に
*.mp3
パターンが正しく適用されます。 ファイルを再帰的に処理する必要がある場合は、 UsingFindを使用するか、Bash 4
shopt -s globstar
詳しく見てください。
質問: * .mp3パターンに一致するファイルが現在のフォルダーにない場合はどうなりますか?
for
ループは
i="*.mp3"
で1回実行されますが、これは予期される動作ではありません! この問題の解決策として、適切なファイルの存在の確認を使用できます。
# POSIX for i in *.mp3; do [ -e "$i" ] || continue some command "$i" done
別の解決策は、
shopt -s nullglob
Bashプロパティを使用すること
shopt -s nullglob
。 ただし、これはドキュメントを読んで、このスクリプトの他のすべてのグローブに対するこの設定の影響を慎重に評価した後にのみ行うことができます。 ループ本体の
$i
囲む引用符に注意してください。 これは、2番目の問題につながります。
2. cp $ file $ターゲット
このチームの何が問題になっていますか? 基本的に、
$file
と
$target
スペースやワイルドカードが含まれていないことが事前にわかっていれば問題ありません。 ただし、置換の結果は引き続きWordSplittingとファイルパスの置換の対象となります。 したがって、パラメーターの展開は常に二重引用符で囲みます。
cp -- "$file" "$target"
それ以外の場合は、
cp 01 - Don't Eat the Yellow Snow.mp3 /mnt/usb
cp: cannot stat `01': No such file or directory
cp 01 - Don't Eat the Yellow Snow.mp3 /mnt/usb
cp: cannot stat `01': No such file or directory
ようなエラーになり
cp: cannot stat `01': No such file or directory
。
$file
にワイルドカード文字( *または?または[ )が含まれる場合、一致するファイルがある場合にのみ展開されます。 二重引用符を使用すると、「$ファイル」の先頭に「-」が現れるまですべて問題ありません。 この場合、
cp
はコマンドラインオプションをフィードしようとしていると判断します(次の章を参照)。
いくつかの異常な状況でも、変数の内容を保証できる場合、特にファイル名が含まれている場合、パラメーターを引用符で囲むことは適切で一般的な方法です。 経験豊富なスクリプト作成者は常に引用符を使用します。ただし、まれに、コードコンテキストからパラメーターに保証された安全な値が含まれていることが明らかな場合を除きます。 専門家は、おそらく
cp
コマンドヘッダーの使用は間違いであると判断するでしょう。 あなたもそう思うべきです。
3.前にハイフンが付いたファイル名
ハイフンが前に付いたファイル名は、多くの問題を引き起こす可能性があります。
*.mp3
ようなグローブ
*.mp3
(現在のロケールに応じて)拡張リストにソートされ、ほとんどのロケールではハイフンが最初にソートされ、次に文字がソートされます。 次に、リストが
-filename
をオプションとして誤って解釈する可能性のあるコマンドに渡されます。 この状況には2つの主な解決策があります。
1つ目は、コマンド(例:
cp
)とその引数の間に2つのハイフン(
--
)を挿入することです。 これはオプションの検索を停止するためのシグナルとなり、すべてが正常になります。
cp -- "$file" "$target"
しかし、このアプローチには問題があります。 必ず挿入する必要があります
--
コンテキストでパラメーターを使用するたびに 、オプションとして解釈できる場合。 そして、これは多くの冗長性を意味し、簡単に何かを見逃す可能性があります。
解析オプション用の適切に作成されたライブラリのほとんどはこれを理解しており、正しく使用されるプログラムはこの機能を無料で継承する必要があります。 ただし、オプションの終わりを認識する責任はアプリケーションにのみあることに留意してください。 オプションを手動で解析するプログラム、誤って解析するプログラム、またはサードパーティのライブラリを使用するプログラムでは、末尾が認識されない場合があります。 POSIXで説明されているいくつかの例外を除き、標準ユーティリティはこれを行う必要があります。 たとえば、
echo
。
別のオプションは、ファイル名が常にフォルダーで始まることを確認することです。 このために、相対または絶対ファイルパスが使用されます。
for i in ./*.mp3; do cp "$i" /target ... done
この場合、名前がハイフンで始まるファイルがある場合でも、globのおかげで、変数には常に
./-foo.mp3
ようなものが含まれることを確認できます。 そして、これは
cp
に関しては完全に安全です。
最後に、すべての結果に同じ接頭辞が付き、変数がループの本体で数回しか使用されないことを保証できる場合、置換時に接頭辞を単純に連結できます。 理論的には、各単語のいくつかの余分な文字の生成と保存が節約されます。
for i in *.mp3; do cp "./$i" /target ... done
4. [$ foo = "bar"]
この状況は、第2章で説明した問題に非常に似ています。 しかし、それでも非常に重要なので繰り返します。 ヘッダー行の引用符は、必要な場所ではありません。 Bashでは、文字列リテラルを引用する必要はありません (メタ文字またはパターン文字が含まれている場合を除く)。 ただし、変数にスペースまたはワイルドカードを含めることができる場合は、変数を引用符で囲む必要があります。
この例は、いくつかの理由で壊れる可能性があります。
-
[
参照される変数が存在しないか空の場合、コマンド[
は最終的に次のようになります。
[ = "bar" ] # !
...エラーをスローします:unary operator expected.
(=
演算子は単項ではなく二項なので、コマンド[
は彼を見るとショックを受けます)。
- 変数に内部スペースが含まれている場合、コマンド
[
前に単語に分割されます。 したがって、次のようになります。
[ multiple words here = "bar" ]
問題は表示されないかもしれませんが、[
使用したおかげで構文エラーが発生しました。 正しい書き方:
# POSIX [ "$foo" = bar ] # !
これは、POSIXコマンドでは[
渡される引数の数に応じてアクションを定義するため、$foo
前にハイフンがあっても、POSIX互換の実装では正常に機能します。 非常に古いシェルでのみこれに関する問題が発生します。コードを記述するときにそれらについて心配することはできません(以下のx "$foo
"のトリックを参照)。
Bashや他の多くのkshのようなシェルには、 [[を使用した優れた代替手段があります。
# Bash / Ksh [[ $foo == bar ]] # !
=
[[ ]]
の左側にある変数は、単語の分割やグロビングの影響を受けないため、引用符で囲む必要はありません。 また、空の変数も正しく処理されます。 一方、引用符を使用しても害はありません。
[
および
test
とは異なり、
==
も使用できます。
[[
を使用して比較する場合、単純な文字列比較ではなく、右側の文字列に対してパターンマッチングが実行されることに注意してください。 正しい文字列をリテラルにするには、パターンマッチングのコンテキストで特別な意味を持つ文字を使用するときに引用符で囲む必要があります。
# Bash / Ksh match=b*r [[ $foo == "$match" ]] # ! , b*r.
あなたはおそらく同様のコードを見てきました:
# POSIX / Bourne [ x"$foo" = xbar ] # , .
非常に古いシェルで実行されるコードの場合、ハックx "
$foo
"が必要です。 ここで、
[[
より原始的な
[
。
$foo
がハイフンで始まる場合、混乱が生じます。 古いシステムでは、
[
=
の右側のトークンがハイフンで始まるかどうかは気にしません。 彼女はそれを文字通り使用しています。 そのため、左側に注意する必要があります。
この回避策を必要とするシェルはPOSIX互換ではないことに注意してください。 Heirloom Bourneでさえこれを必要としません(おそらくこれは、最も一般的なシステムシェルの1つであるBourneシェルのPOSIXクローンではない可能性があります)。 このような極端な移植性が要求されることはめったになく、コードが読みにくくなり、美しくなります。
5. cd $(ディレクトリ名「$ f」)
引用符に関連する別のエラー。 変数展開の場合と同様に、コマンド置換の結果は、単語とファイルパスの置換に分割されます。 したがって、引用符で囲みます。
cd -P -- "$(dirname -- "$f")"
引用符のネストの背後にあるロジックは、ここでは完全に明らかではありません。 Cプログラマーは、1番目と2番目の二重引用符がグループ化され、3番目と4番目の二重引用符が結合されることを期待します。 しかし、Bashでは、すべてが異なります。 Bashは、コマンド置換内の二重引用符を1つのペアとして扱い、置換外の二重引用符を別のペアとして扱います。
別の方法で記述することもできます。パーサーはコマンド置換を「ネストレベル」として扱い、内部の引用符は外部の引用符とは別に移動します。
6. ["$ foo" = bar && "$ bar" = foo]
古いテスト(または[)コマンド内では
&&
を使用できません。 Bashパーサーは、
[[ ]]
または
(( ))
外側で
&&
を検出し、その結果、コマンドを
&&
前後で2つのチームに分割します。 代わりに、次の2つのオプションのいずれかを使用します。
[ bar = "$foo" ] && [ foo = "$bar" ] # ! (POSIX) [[ $foo = bar && $bar = foo ]] # ! (Bash / Ksh)
(第4章で述べた遺産のために、定数と変数を
[
内で交換しました。また、
[[
交換することもできますが、テンプレートとして解釈しないようにするには、引用符を使用する必要があります)
同じことが
||
も当てはまります 。
[[
または2つのコマンド
[
使用します。
これを避けてください:
[ bar = "$foo" -a foo = "$bar" ] # .
バイナリ演算子
-a
、
-o
および(/)(グループ化)は、POSIX標準に対するXSI拡張です。 POSIX-2008では、これらはすべて非推奨としてマークされています。 新しいコードでそれらを使用する価値はありません。
[ A = B -a C = D ]
(または
-o
)に関連する実際的な問題の1つは、 POSIXが
test
の結果を決定しないこと、または4つ以上の引数を使用することです。 これはおそらくほとんどのシェルで機能しますが、期待することはできません。 POSIXシェル用に作成する必要がある場合は、
&&
演算子で区切られた2つの
test
または
[
コマンドを使用します。
7. [[$ foo> 7]]
ここにはいくつかのポイントがあります。 まず、
[[
は算術式を計算するためだけに使用すべきではありません。 サポートされているテストステートメントの1つを含むテスト式に使用する必要があります。 技術的には
[[
演算子を使用して計算を実行できますが 、式のどこかに存在する非数学的なテスト演算子の1つと組み合わせてこれを行うことは意味があります。 数値データを比較するだけの場合(または他の算術演算を実行する場合)は、
(( ))
を使用する方がはるかに優れています。
# Bash / Ksh ((foo > 7)) # ! [[ foo -gt 7 ]] # , . . ((...)) let.
[[ ]]
内で>演算子を使用すると、システムはこれを数値ではなく、文字列データの比較(ロケールによるソート順のチェック)として扱います。 時々これは機能するかもしれませんが、それはあなたがそれを最も期待していない時にちょうどあなたを失敗させます。 さらに悪いことに、use> inside
[ ]
:これは出力のリダイレクトです。 7という名前のファイルがフォルダーに表示され、
$foo
何かがあるまでテストは正常に実行されます。
POSIXとの厳密な互換性が必要で、コマンド
((
、その後、昔ながらの
[
::
# POSIX [ "$foo" -gt 7 ] # ! [ $((foo > 7)) -ne 0 ] # POSIX (( .
$foo
整数でない場合、
test ... -gt
は失敗することに注意してください。 したがって、引用符で囲むことはパフォーマンスのためだけに意味があり、一部のシェルで副作用の可能性を減らすために引数を単一の単語に分割します。
算術コンテキスト(
((
または
let
)を含む)または数値比較を含むテスト式
[
の入力を保証できない場合、計算を実行する前に常に入力を検証する必要があります。
# POSIX case $foo in *[![:digit:]]*) printf '$foo expanded to a non-digit: %s\n' "$foo" >&2 exit 1 ;; *) [ $foo -gt 7 ] esac
8. grep foo bar | 読み取り中-r; do((count ++)); やった
このコードは正常に見えますか? もちろん、これは
grep -c
単なる平凡な実装ですが、これは単純にするために行われています。 各パイプラインコマンドは個別のサブシェルで実行されるため、
count
変更は
while
境界を越えて伝播しません。 ある時点で、これはBashの初心者を驚かせます。
POSIXは、パイプラインの最後の要素をサブシェルで評価する必要があるかどうかを判断しません。
shopt -s lastpipe
いるksh93やBash> = 4.2などの一部のシェルは、サンプルのシェルプロセスで
while
を実行し、副作用を引き起こす可能性があります。 したがって、ポータブルスクリプトは、このような動作に依存しないように作成する必要があります。
この問題や同様の問題を解決する方法は、 Bash FAQ#24から見つけることができます。 ここで説明するには長すぎます。
9. if [grep foo myfile]
多くの初心者は、このキーワードの直後に
[
または
[[
続くことが多いため、
if
式について誤解しています。 Cの
if
で使用される単純な括弧のように、人々は
[
何らかの理由で
if
構文の一部であると考えます。それは真実ではありません! コマンドを取得した
if
。 それは
[
、それは
if
構文マーカーではない。 このコマンドは、最後の引数が
]
なければならないことを除いて、
test
と同等です。 例:
# POSIX if [ false ]; then echo "HELP"; fi if test false; then echo "HELP"; fi
これらの行は同等です。どちらも、false引数が空でないことを確認します。 どちらの場合も、HELPが出力されます。他の言語から来て、シェル構文を理解しようとしているプログラマーの驚きです。
if
構文
if
次のとおりです。
if COMMANDS then <COMMANDS> elif <COMMANDS> # optional then <COMMANDS> else <COMMANDS> # optional fi # required
もう一度-
[
はチームです。 彼女は他の普通のチームのように議論を受け取ります。
if
は、他のコマンドを含む複合コマンドです。 そしてその構文は[ !
Bashには組み込みコマンド
[
があります。したがって、
[
、
]
については何も特別なことはありません。 Bashはコマンドへの引数として
]
を渡すだけであり、コマンドは最後の引数である必要があります。そうでない場合、スクリプトは見苦しくなります。
0個以上のオプションの
elif
セクションと、1つのオプションの
else
セクションがある場合があり
else
。
複合
if
コマンドには、コマンドのリストが配置される2つ以上のセクションが含まれます。 各セクションは、キーワード
then
、
elif
または
else
で始まり、キーワード
fi
終わります。 最初のセクションと後続の各
elif
セクションの最後のコマンドの完了コードは、対応する各
then
セクションの計算を決定します。 他の
elif
セクションは、
then
1つが実行される前に評価されます。
then
セクションが計算されない場合、
else
ブランチへの切り替えが発生します。
else
がない場合、
if
ブロックは完了し、結果の
if
コマンドは0(true)を返します。
grep
出力に応じて決定を行いたい場合は、括弧や角括弧、バックティック、またはその他の構文で囲む必要はありません!
if
後にコマンドとして
grep
を使用します。
if grep -q fooregex myfile; then ... fi
grep
が
myfile
の文字列で一致を検出した場合、完了コードは0(true)になり、
then
部分が実行されます。 一致するものが見つからない場合、
grep
は0以外の値を返し、結果の
if
コマンドはゼロになります。
また読む:
10. if [bar = "$ foo"]; その後...
[bar="$foo"] # ! [ bar="$foo" ] # !
前の章で説明したように、
[
はコマンドです(これは
type -t [
または
whence -v [
)
type -t [
で証明できます。 他の単純なコマンドと同様に、Bashはスペースがそれに続くことを期待し、次に最初の引数、スペースをもう一度、などと期待します。 スペースを無視することはできません! 正しいスペルは次のとおりです。
if [ bar = "$foo" ]; then ...
各コンポーネント(
bar
、
=
、置換「
$foo
」、および
]
)は、
[
。 , , .
11. if [ [ a = b ] && [ c = d ] ]; then ...
.
[
. ,
if
- «», .
[
. -
if
Bash-, !
, :
if [ a = b ] && [ c = d ]; then ...
,
if
,
&&
( AND, ). , :
if test a = b && test c = d; then ...
test
false,
if
. true,
test
; true,
if
. (C-
&&
. Bash . ,
||
OR .)
[[
&&
, :
if [[ a = b && c = d ]]; then ...
6 ,
test
.
12. read $foo
$
read
.
foo
, :
read foo
:
IFS= read -r foo
read $foo
/
$foo
. ,
foo
; .
13. cat file | sed s/foo/bar/ > file
. , , :
- (clobbered) ( 0 , , ),
- ,
- , , .
, , .
printf %s\\n ',s/foo/bar/g' wq | ed -s file
, (*) .
:
sed 's/foo/bar/g' file > tmpfile && mv tmpfile file
GNU sed 4.x:
sed -i 's/foo/bar/g' file(s)
, — .
- Perl 5.x (, , , GNU sed 4.x):
perl -pi -e 's/foo/bar/g' file(s)
Bash FAQ #21 .
(*)
sponge
moreutils :
sed '...' file | grep '...' | sponge file
mv
, «» ( !) ,
file
. , , , .
+ mv
/ . ,
mv
sync
.
14. echo $foo
.
$foo
, , . - Bash- , , . .
msg=", *.zip" echo $msg
, (glob) ( *.zip) . , :
, freenfss.zip lw35nfss.zip
. イラスト:
var="*.zip" # var , "zip" echo "$var" # *.zip echo $var # , .zip
,
echo
. , ,
-n
,
echo
, , . —
printf
:
printf "%s\n" "$foo"
15. $foo=bar
,
$
, . Perl.
16. foo = bar
,
=
, . .
foo = bar
, . —
foo
— . — .
-
foo= bar # !
-
foo =bar # !
-
$foo = bar; # !
-
foo=bar # .
-
foo="bar" # .
17. echo <<EOF
Here- — . . ,
echo
stdin.
# : echo <<EOF Hello world How's it going? EOF # : cat <<EOF Hello world How's it going? EOF # , (, echo ): echo "Hello world How's it going?"
. . . , ,
cat
, :
# printf ( , printf ): printf %s "\ Hello world How's it going? "
printf
, \ . ( ).
\n
printf
. \ . , , «» :
printf %s \ 'Hello world ' printf %s 'Hello world '
18. su -c 'some command'
. ,
su
-c
, , . OpenBSD:
$ su -c 'echo hello' su: only the superuser may specify a login class
-c 'some command'
,
-c
.
su root -c 'some command' # Now it's right.
su
root-, . , . .
19. cd /foo; bar
cd
,
bar
. , , ,
bar
rm -f *
.
cd
. :
cd /foo && bar
cd
, :
cd /foo || exit 1 bar baz bat ... # Lots of commands.
cd
, stderr- «bash: cd: /foo: No such file or directory». stdout , :
cd /net || { echo >&2 "Can't read /net. Make sure you've logged in to the Samba network, and try again."; xit 1; } do_stuff more_stuff
,
{
echo
.
}
;
。
set -e , , , . ( , ).
, Bash-,
pushd
,
popd
dirs
. , ,
cd
pwd
, . :
find ... -type d -print0 | while IFS= read -r -d '' subdir; do here=$PWD cd "$subdir" && whatever && ... cd "$here" done
C :
find ... -type d -print0 | while IFS= read -r -d '' subdir; do (cd "$subdir" || exit; whatever; ...) done
cd
. , ,
cd
. ,
... && ...
, . ( ).
20. [ bar == "$foo" ]
==
POSIX-
[
.
=
[[
.
[ bar = "$foo" ] && echo yes [[ bar == $foo ]] && echo yes
Bash
[ "$x" == y ]
, . — «» ( Bashism ). ,
[[
.
21. for i in {1..10}; do ./something &; done
;
&
.
;
。
for i in {1..10}; do ./something & done : for i in {1..10}; do ./something & done
&
(command terminator),
;
。 .
,
;
,
;
。
22. cmd1 && cmd2 || cmd3
-
&&
||
if ... then ... else ... fi
. :
[[ -s $errorlog ]] && echo "Uh oh, there were some errors." || echo "Successful."
if ... fi
. ,
&&
, . «true» (0), ,
||
。 例:
i=0 true && ((i++)) || ((i--)) echo $i # Prints 0
ここで何が起こっていますか? ,
i
1, 0. ?
i++
,
i--
.
((i++))
, . 0 (
i
), , 0, false .
((i++))
(
i
0) 1 (false),
((i--))
.
, ,
++i
true:
i=0 true && (( ++i )) || (( --i )) echo $i # Prints 1
.
x && y || z
, y ! ,
i
-1 0.
, , - , ,
if ... fi
.
i=0 if true; then ((i++)) else ((i--)) fi echo $i # Prints 1
Bourne:
true && { echo true; false; } || { echo false; true; }
«true» «false», «true».
23. echo «Hello World!»
Bash ( 4.3), :
bash: !": event not found
, , Bash (history expansion) csh, . , . , «» :
$ echo "hi\!" hi\!
—
histexpand
.
set +H
set +o histexpand
:
:
histexpand
, ?
,
mp3info -t "Don't Let It Show" ... mp3info -t "Ah! Leah!" ...
, . . , , . . ,
~/.bashrc. --
GreyCat
:
echo 'Hello World!'
または
set +H echo "Hello World!"
または
histchars=
set +H
set +o histexpand
~/.bashrc
, . , , .
:
exmark='!' echo "Hello, world$exmark"
Bash 4.3 ,
!
. ,
echo "Hello World!"
, :
echo "Hello, World!(and the rest of the Universe)" echo "foo!'bar'"
24. for arg in $*
Bash ( Bourne-) , .
$*
,
$@
. , . :
for arg in "$@" # : for arg
,
for arg
for arg in "$@"
.
"$@"
— , ( ). 99% .
例:
# for x in $*; do echo "parameter: '$x'" done $ ./myscript 'arg 1' arg2 arg3 parameter: 'arg' parameter: '1' parameter: 'arg2' parameter: 'arg3'
:
# for x in "$@"; do echo "parameter: '$x'" done # : for x; do echo "parameter: '$x'" done $ ./myscript 'arg 1' arg2 arg3 parameter: 'arg 1' parameter: 'arg2' parameter: 'arg3'
25. function foo()
, .
function
()
. Bash ( ) . (, zsh 4.x , , ).
function foo
, :
foo() { ... }
26. echo "~"
, '~' . echo stdout '~', . , , $HOME, '~'. , $HOME — "/home/my photos".
"~/dir with spaces" # "~/dir with spaces" ~"/dir with spaces" # "~/dir with spaces" ~/"dir with spaces" # "/home/my photos/dir with spaces" "$HOME/dir with spaces" # "/home/my photos/dir with spaces"
27. local varname=$(command)
,
local
. . , (
$?
) , . . :
local varname varname=$(command) rc=$?
.
28. export foo=~/bar
, — — ( ). ,
=
.
export
local
. ( Bash)
export foo=~/bar
, ( dash) — .
foo=~/bar; export foo # ! export foo="$HOME/bar" # !
29. sed 's/$foo/good bye/'
$foo
. —
$
. :
foo="hello"; sed "s/$foo/good bye/"
: escapes. « ».
30. tr [AZ] [az]
. :
[AZ]
[az]
. , , . , . , 3 .
:
tr
. , '[' '[', - AZ az, ']' ']'. , .
, , AZ az 26 ASCII-. z ! , :
# , 26 LC_COLLATE=C tr AZ az # , . tr '[:upper:]' '[:lower:]'
, .
31. ps ax | grep gedit
, . gedit. - , gedit ( ). , . PID gedit (),
$ ps ax | grep gedit 10530 ? S 6:23 gedit 32118 pts/0 R+ 0:00 grep gedit
, Race Condition , grep. :
ps ax | grep -v grep | grep gedit # ,
:
ps ax | grep '[g]edit' # , shell GLOB
Grep ,
[g]edit
,
grep
gedit
.
GNU/Linux –C :
$ ps -C gedit PID TTY TIME CMD 10530 ? 00:06:23 gedit
,
pgrep
?
$ pgrep gedit 10530
PID
awk
cut
:
$ ps -C gedit | awk '{print $1}' | tail -n1
ps
:
$ ps -C gedit -opid= 10530
1992
pgrep
, ,
pidof
( GNU/Linux):
$ pidof gedit 10530
PID, ,
pkill
. , , ,
pgrep/pkill ssh
sshd, .
, . , Firefox firefox-bin. , , ps ax | grep firefox . pgrep:
$ pgrep -fl firefox 3128 /usr/lib/firefox/firefox 7120 /usr/lib/firefox/plugin-container /usr/lib/flashplugin-installer/libflashplayer.so -greomni /usr/lib/firefox/omni.ja 3128 true plugin
. 真剣に。
32. printf "$foo"
, .
$foo
,
\
%
. :
printf %s "$foo" printf '%s\n' "$foo"
33. for i in {1..$n}
Bash .
$n
, , . , runtime. :
for ((i=1; i<=n; i++)); do ... done
for
, , (pre-expands), .
34. if [[ $foo = $bar ]] ( )
,
=
[[, Bash , .
bar
*, true. , :
if [[ $foo = "$bar" ]]
, , , . .
,
=~
, , . .
35. if [[ $foo =~ 'some RE' ]]
, . , , :
re='some RE' if [[ $foo =~ $re ]]
=~
Bash. .
[[
:
[[ $foo = "*.glob" ]] # Wrong! *.glob is treated as a literal string. [[ $foo = *.glob ]] # Correct. *.glob is treated as a glob-style pattern.
36. [ -n $foo ] or [ -z $foo ]
[
, .
$foo
0 , 42 , , 1, .
[ -n "$foo" ] [ -z "$foo" ] [ -n "$( - "$file")" ] # [[ , : [[ -n $foo ]] [[ -z $foo ]]
37. [[ -e "$broken_symlink" ]] 1 $broken_symlink
Test symlink', , symlink , — , , , —
test –e
1, symlink. ( ), :
# bash/ksh/zsh [[ -e "$broken_symlink" || -L "$broken_symlink" ]] # POSIX sh+test [ -e "$broken_symlink" ] || [ -L "$broken_symlink" ]
38. ed file <<<«g/d\{0,3\}/s//e/g»
, ed 0 \{0,3\}. , :
ed file <<<"g/d\{1,3\}/s//e/g"
, POSIX-, BRE ( , ed), 0 (. 5) .
39. (sub-string) expr «match»
…
word=abcde expr "$word" : ".\(.*\)" bcde
«match»
word=match expr "$word" : ".\(.*\)"
, «match» — . GNU '+'
word=match expr + "$word" : ".\(.*\)" atch
expr
. , , ( Parameter Expansion ). , ? POSIX- (Substring Expansion):
$ word=match $ echo "${word#?}" # PE atch $ echo "${word:1}" # SE atch
,
expr
, Solaris POSIX
/bin/sh
. , , . , , , .
40. UTF-8 (Byte-Order Marks, BOM)
: Unix UTF-8 BOM. , MIME- . BOM UTF-8, , ( - ) , , , . , BOM, , , MS-DOS.
: , UTF-8 8- , BOM , , ASCII-, "#!" Unix-».
http://unicode.org/faq/utf_bom.html#bom5
41. content=$(<file)
, , ( :
`...`
,
$(...)
,
$(<file)
,
`<file`
${ ...; }
(ksh)) . , , , , , , . , : .
absolute_dir_path_x=$(readlink -fn -- "$dir_path"; printf x) absolute_dir_path=${absolute_dir_path_x%x}
, :
read
.
# Ksh (or bash 4.2+ with lastpipe enabled) readlink -fn -- "$dir_path" | IFS= read -rd '' absolute_dir_path
,
read
false, NUL-, . —
PIPESTATUS
. NUL-,
read
true,
pipefail
.
set -o pipefail { readlink -fn -- "$dir_path"; printf '\0x'; } | IFS= read -rd '' absolute_dir_path
: Bash
pipefail
PIPESTATUS
, ksh93 —
pipefail
, mksh
pipefail
, —
PIPESTATUS
. , ksh93,
read
NUL-.
42. for file in ./*; do if [[ $file != *.* ]]
, (. 3).
./
.
*.*
,
./filename
. . , (, , ), :
[[ $file != ./*.* ]]
, .
# Bash shopt -s nullglob for path in ./*; do [[ ${path##*/} != *.* ]] && rm "$path" done # for file in *; do [[ $file != *.* ]] && rm "./${file}" done # for file in *.*; do rm "./${file}" done
—
--
( 3).
shopt -s nullglob for file in *; do [[ $file != *.* ]] && rm -- "$file" done
43. somecmd 2>&1 >>logfile
, , , , stdout stderr. , stderr . , , . , . : « , (tty), -». . tty. :
somecmd >>logfile 2>&1
, Copy BashGuide — redirection .
44. cmd; ((! $? )) || die
$?
, . , ( ), , :
if cmd; then ... fi
:
cmd status=$? case $status in 0) echo success >&2 ;; 1) echo 'Must supply a parameter, exiting.' >&2 exit 1 ;; *) echo "Unknown error $status, exiting." >&2 exit "$status" esac
45. y=$(( array[$x] ))
POSIX (arithmetic expansion) ( ), (array subscript) . , , .
$ x='$(date >&2)' # , , $ y=$((array[$x])) # Mon Jun 2 10:49:08 EDT 2014
:
$ y=$((array["$x"])) Mon Jun 2 10:51:03 EDT 2014
:
# 1. $x, . $ y=$((array[\$x])) # 2. ${array[$x]}. $ y=$((${array[$x]}))
46. read num; echo $((num+1))
(. BashFAQ/054 ), num , .
$ echo 'a[$(echo injection >&2)]' | bash -c 'read num; echo $((num+1))' injection 1
47. IFS=, read -ra fields <<< "$csv_line"
, POSIX IFS (field terminator), . , , :
$ IFS=, read -ra fields <<< "a,b," $ declare -p fields declare -a fields='([0]="a" [1]="b")'
? (« »). Bash, . :
$ IFS=, read -ra fields <<< "a,b,c" $ declare -p fields declare -a fields='([0]="a" [1]="b" [2]="c")'
? , IFS- . , IFS- «» , . , IFS- , , .
$ input="a,b," $ IFS=, read -ra fields <<< "$input," $ declare -p fields declare -a fields='([0]="a" [1]="b" [2]="")'
48. export CDPATH=.:~/myProject
CDPATH. CDPATH .bashrc , , Bash- sh-,
cd
, . . , :
cd some/dir || exit cmd to be run in some/dir
./some/dir
~/myProject/some/dir
, , .
cd
, , , .
—
cd
, :
output=$(cd some/dir && some command)
CDPATH ,
cd
stdout
/home/user/some/dir
, , CDPATH,
some command
.
CDPATH, ,
./
.
unset CDPATH
, , , CDPATH.