質問
- 私はディレクトリを再帰的に検索し、一意の隠しファイルおよび/または隠されたディレクトリのすべてのサブディレクトリ(最大深度まで)を含むパスを返すことができるBashコマンドを作成しようとしました。
視覚的な説明
- 次のファイルシステムの抜粋を検討してください。
+--- Root_Dir
| +--- Dir_A
| | +--- abc.txt
| | +--- 123.txt
| | +--- .hiddenfile
| | +--- .hidden_dir
| | | +--- normal_sub_file_1.txt
| | | +--- .hidden_sub_file_1.txt
| |
| +--- Dir_B
| | +--- abc.txt
| | +--- .hidden_dir
| | | +--- normal_sub_file_2.txt
| | | +--- .hidden_sub_file_2.txt
| |
| +--- Dir_C
| | +--- 123.txt
| | +--- program.c
| | +--- a.out
| | +--- .hiddenfile
| |
| +--- Dir_D
| | +--- .hiddenfile
| | +--- .another_hiddenfile
| |
| +--- Dir_E
| | +--- .hiddenfile
| | +--- .hidden_dir
| | | +--- normal_sub_file_3.txt # This is OK because its within a hidden directory, aka won't be checked
| | | +--- .hidden_sub_file_3.txt
| |
| +--- Dir_F
| | +--- .hidden_dir
| | | +--- normal_sub_file_4.txt
| | | +--- .hidden_sub_file_4.txt
希望の出力
- 私が探しているコマンドが出力されます。
./Root_Dir/Dir_D ./Root_Dir/Dir_E ./Root_Dir/Dir_F
Dir_D
隠しファイルだけが含まれているからです。Dir_E
私が探しているレベルの隠しファイルと隠されたディレクトリだけが含まれているからです。Dir_F
私が探しているレベルの隠しディレクトリだけが含まれているからです。
努力する
- そのコマンドを使用して
find
目的の結果を取得しようとしていますが、出力をパイプするために必要な他のコマンドが何であるか、使用する必要がある他のオプションが何であるかがわかりません。 - 有効なコマンドは次のとおりです。
bob@linux:/$ find ./Root_Dir -mindepth 2 -maxdepth 2 -type d -name "*." -type -f -name "*." | command to see if these are the only files in that directory
答え1
私はこれに対してあまり良くない解決策を持っています。
スクリプト形式:
#!/bin/bash
# Echo whatever is passed to fail, and then exit with a status of 1
fail() {
echo >&2 "$@"
exit 1
}
# If the number of arguments are less or more than what
# we expect, throw help syntax and exit.
if [ $# -gt 2 ] || [ $# -lt 2 ]
then
fail "
$0 check for directories that only contain hidden listings
Usage: $0 <Directory to search from> <Depth to check>
"
fi
# Assign parameters
root_dir=$1
depth=$2
# Find a list of directories that contain files OR subdirectories
# that are hidden
readarray -t dirs < <(export LC_ALL=C
find "$root_dir" -mindepth "$depth" -maxdepth "$depth" \
-name ".*" \( -type d -o -type f \) -execdir pwd \; | sort -u
)
# Of the dirs we found, do any of them return any listings with a
# default ls execution? If so, that directory cannot only contain
# hidden listings.
final=()
for dir in "${dirs[@]}"
do
notExclusive="$(ls -- "$dir")"
if [ "$notExclusive" = "" ]
then
final+=("$dir")
fi
done
# The array final contains the directories who only contain hidden
# files/subdirectories.
printf '%s\n' "${final[@]}"
デフォルトでは、隠しリスト(質問で指定された深さ2)を含むディレクトリを見つけて配列にロードし、ls
フラグが何も返さない場合は、ディレクトリに隠されたリストのみが含まれており、標準を満たすと結論付けることができます。
find
OPの説明に基づいて一度だけ呼び出す理由を説明してください。コマンドfind
には基本ロジックの演算子があります。 -aは、論理ANDで2つの式を結合するときのデフォルトの動作です。論理NOTでは、-oは論理ORと同じです。
この方法は、ディレクトリパスに改行文字が含まれていないと仮定し、含まれている場合はreadarray
各ディレクトリパスを誤って区別します。
醜い「一行」:
readarray -t dirs < <(export LC_ALL=C; find ./Root_Dir -mindepth 2 -maxdepth 2 -name '.*' \( -type d -o -type f \) -execdir pwd \; | sort -u); final=(); for dir in "${dirs[@]}"; do notExclusive="$(ls -- "$dir")"; if [ "$notExclusive" = "" ]; then final+=("$dir"); fi; done; printf '%s\n' "${final[@]}"
答え2
find
と を使用して、bash
次のコマンドはすべてのディレクトリを検索します。その後、bash
見つかったディレクトリに隠されていない名前が含まれているかどうかをテストするために使用されます。これは*
、各ディレクトリのglobパターンを拡張し、結果名の数を数えることによって行われます。数値が 0 の場合、出力ディレクトリのパス名が出力されます。デフォルトでは、グローバルモードは*
隠された名前には拡張されません。
find . -type d -exec bash -O nullglob -c '
for dirpath do
set -- "$dirpath"/*
[[ $# -eq 0 ]] && printf "%s\n" "$dirpath"
done' bash {} +
一致するものがない場合は、パターン自体を削除するようにnullglob
インラインスクリプトにシェルオプションを設定しました。bash -c
を使用するとシェルオプション/bin/sh
にアクセスできないため、nullglob
パターンが展開される個々の名前が実際に存在するかどうかをテストする必要があります。それ以外の場合、ディレクトリには隠された名前のみが含まれます。
find . -type d -exec sh -c '
for dirpath do
set -- "$dirpath"/*
[ "$#" -eq 1 ] && [ ! -e "$1" ] && printf "%s\n" "$dirpath"
done' sh {} +
発見を避けたい場合空ディレクトリは! -empty
ホームディレクトリの前に追加されます(あなたの実装が一般的なテストを通してこの非標準をサポートすると仮定します)。-exec
find
find
答え3
そしてzsh
:
print -rC1 -- **/*(NDF^e['()(($#)) $REPLY/*(NY1)'])
特定の深さ以上のディレクトリを除外するには、~
除外演算子を使用して除外できます。
set -o extendedglob
print -rC1 -- **/*~*/*/*(NDF^e['()(($#)) $REPLY/*(NY1)'])
zsh
しかし、それが最初に彼らの中に降りるのを止めないことに注意してください。
深さ2だけが必要な場合は、次のことができます。
print -rC1 -- */*(NDF^e['()(($#)) $REPLY/*(NY1)'])
(ただし、深さ1ではシンボリックリンクに従います)
print -rC1
列にr
awを印刷します。1
C
**/*
再帰ワイルドカード(...)
グローバル予選N
:N
ulglob:一致するものがなくても文句を言わないでください。D
:D
otglob:隠しファイルを無視しないでくださいF
:ディレクトリのみが空です(F
およびを除く1つ以上のエントリ)。.
..
^
:次の修飾子を否定します。e['code']
:ファイルを選択するかどうかをe
評価します。code
()(($#)) args
:1つ以上のパラメータが渡された場合はtrueを返します。$REPLY
でcode
現在検討中のファイルを保存します。$REPLY/*
隠されていないファイル$REPLY
Y1
:最初のファイルを見つけて停止します。