BashとDashを使用すると、シェルを使用して空のディレクトリを確認できます(単純化のためにドットファイルは無視されます)。
set *
if [ -e "$1" ]
then
echo 'not empty'
else
echo 'empty'
fi
しかし、私は最近この場合Zshが失敗することを知りました。
% set *
zsh: no matches found: *
% echo "$? $#"
1 0
したがって、コマンドがset
失敗するだけでなく設定もできません$@
。$#
isかどうかをテストできると思いましたが、0
Zshが実行を中断したようです。
% { set *; echo 2; }
zsh: no matches found: *
BashとDashとの比較:
$ { set *; echo 2; }
2
bash、dash、zshで動作するようにこれを実行できますか?
ベストアンサー1
これにはいくつかの問題があります
set * if [ -e "$1" ] then echo 'not empty' else echo 'empty' fi
パスワード:
- この
nullglob
オプションを有効にすると(以前は他のほとんどのシェルでサポートされていた)、すべてのシェル変数(および一部のシェルの関数)が代わりに一覧表示されます。zsh
set *
set
- 非表示の最初のファイル名がまたは
-
で始まる場合は、+
オプションとして扱われますset
。 zshとkshのいくつかの実装/バージョンでは+A[$(reboot)]
。set -- *
*
非表示のファイルのみが拡張されるため、ディレクトリが空であることをテストするのではなく、ディレクトリに非表示のファイルが含まれているかどうかをテストします。一部のシェルでは、dotglob
またはオプションを使用するか、特殊変数(シェルに応じて)をglobdot
使用して問題を解決できます。FIGNORE
[ -e "$1" ]
stat()
システムコールが成功したかどうかをテストします。最初のファイルがアクセスできない場所へのシンボリックリンクの場合、falseが返されます。ディレクトリが空であることを確認するためにファイルは必要ありませんstat()
(または必要ありませんlstat()
)。内容があることを確認するだけです。*
拡張機能は現在ディレクトリを開き、すべての項目を検索し、非表示になっていないすべての項目を保存して並べ替える必要があります。これは非常に非効率的です。
ディレクトリが空でないこと(およびを除くすべてのエントリがあるかどうか)を確認する最も効率的な方法は、glob修飾子を使用する.
ことです( for..
zsh
F
F
いっぱい、ここでは空ではないことを意味します):
if [ .(NF) ]; then
print . is not empty
else
print "it's empty or I can't read it"
fi
N
nullglob
グローバル予選です。したがって、いっぱいになると.(NF)
拡張され、そうでなければ何もありません。.
.
lstat()
ディレクトリ内のzsh
リンク数が2より大きいと判断された場合は、1つ以上のサブディレクトリがあるため空ではないため、ディレクトリを開く必要さえありません。この場合、読み取り権限がなくてもディレクトリが空ではないことがわかります。それ以外の場合、zshはディレクトリを開き、その内容を読み取った後、すべての内容を読み取ったり、保存したり、並べ替えたりせずに、どちらも最初の.
項目で停止します。..
POSIXシェル(zsh
エミュレーションでPOSIXとしてのみ動作sh
)を使用すると、globを使用してディレクトリが空でないことを確認するのは非常に厄介です。
1つの方法は次のとおりです。
set .[!.]* '.[!.]'[*] .[.]?* [*] *
if [ "$#$1$2$3$4$5" = '5.[!.]*.[!.][*].[.]?*[*]*' ]; then
echo "empty or can't read"
else
echo not empty
fi
(Glob関連のオプションがデフォルト値から変更されず(POSIXのみ指定)、(for)、(for)変数がnoglob
設定されていない(for)有効な文字を形成しないバイトシーケンスを含むファイル名がないとします。 。GLOBIGNORE
bash
FIGNORE
ksh
yash
POSIXシェルでglobが一致しないと拡張されないというアイデアです(これは1970年代後半のBourneシェルで導入されたバグ機能でした)。したがって、==をset -- *
取得すると、一致するものがないかどうか、名前が存在するファイルがあるかどうかはわかりません。$1
*
*
問題を解決する(欠陥のある)方法はを使用することです[ -e "$1" ]
。set -- [*] *
ファイルが存在しない場合は、上記のファイルが残り、隠されたファイル[*] *
に対して同様の操作を実行する*
ため、両方のケースを明確にします。これは、Bourneシェルのもう1つの欠陥(Forsythシェル、pdksh、およびpdkshによって修正された)* *
のためにやや恥ずかしいです。拡張には特別な(医師)項目が含まれており、報告されたとき。zsh
fish
.*
.
..
readdir()
したがって、すべてのシェルで機能させるには、次のようにします。
cwd_empty()
if [ -n "$ZSH_VERSION" ]; then
eval '! [ .(NF) ]'
else
set .[!.]* '.[!.]'[*] .[.]?* [*] *
[ "$#$1$2$3$4$5" = '5.[!.]*.[!.][*].[.]?*[*]*' ]
fi
それにもかかわらず、zsh
基本構文はPOSIX sh構文と互換性がありません。これは、Bourneシェル(POSIX.2が最初にリリースされる前に)のほとんどの主な問題を以前のバージョンと互換性のない方法で修正するためです*
。 Bourneシェル以前のシェルにはこの問題はありcsh
ませtcsh
んfish
でした。.*
含まれていますが、.
分割+globなどの他のいくつかの項目は引数または算術拡張に対して実行されるため、エミュレーションを開かない限り、..
POSIX shで書かれたコードは期待できません。常に動作します。zsh
sh
シミュレーションsh
は特に存在するので、 で行うことができますzsh
。
POSIXでファイルに書き込むには、source
次のようにします。sh
zsh
emulate sh -c 'source ./that-file'
その後、ファイルはシミュレーションで評価され、ファイルで宣言されたすべてのsh
機能は対応するシミュレーションモードのままです。