if条件を使用してforループでアルファベット順を強制します。

if条件を使用してforループでアルファベット順を強制します。

多くのフォルダを作成し、その中でいくつかのことをしたいと思います。フォルダ名は、forループ内で変数として定義された複数の化学元素の配列に基づいています。

for Element in Cr Hf Mo Nb Ta Ti V W Zr

CrHfMoNb、、...などの文字を含むCrHfMoTaサブフォルダを取得できるように、アルファベット順に4つの要素のすべての順列のフォルダが必要です。これを行うために4つのネストされたループを試しましたが、for単純化のためにここでは2つのループだけを使って説明します。私が思いついたコードは次のとおりです。

for Element in Cr Hf Mo Nb Ta Ti V W Zr; do
    for Elemen in Hf Mo Nb Ta Ti V W Zr; do
        mkdir "$Element""$Elemen"N     # the N at the end is intended
    done
done

TiNbNこれにより、目的のフォルダが作成されますが、orZrVNやlikeなどのアルファベット以外の組み合わせも取得されるため、不要なフォルダもたくさん作成されますHfHfN。 3行目にifステートメントを追加すると、重複エントリを削除できます。

do [ "$Element" != "$Elemen" ] && mkdir "$Element""$Elemen"N

しかし、これらの重複フォルダは完全に消えませんでしたが、代わりに私のディレクトリの「ゴースト」ファイルになりました。つまり、HfHfNファイル拡張子なしでetcとして呼び出されました。しかし、実際の問題は残りのフォルダです。次のようなifステートメントを追加してみました。

do [ "$Element" != "$Elemen" ] && [ "$Element" > "$Elemen" ] && mkdir "$Element""$Elemen"N

許容される順列の数は減りますが、何も削除されません。また、ifステートメントを独自のforループに分割してみましたが、何も変更されませんでした。

for Element in Cr Hf Mo Nb Ta Ti V W Zr; do
    [ "$Element" != "$Elemen" ] && [ "$Element" > "$Elemen" ] &&
    for Elemen in Hf Mo Nb Ta Ti V W Zr;  do...

>これが正しいコマンドであるかどうかはわかりませんが、ifこのリストからhttp://tldp.org/LDP/Bash-Beginners-Guide/html/sect_07_01.htmlこれが最も意味のあるようです。同様のコマンドを使用すると-ne, -lt, -le, -gt整数が必要なため、機能しないため、文字は許可されません。結局のところ、4つのループをグループ化してみるのは少し難しくなりました。私は何を見逃していますか?

ベストアンサー1

#/bin/sh

# shellcheck disable=SC2046
# ^ word-splitting by the shell is intentional in this file

elems="Cr Hf Mo Nb Ta Ti V W Zr"
for a in $elems
do
    for b in $elems
    do
        for c in $elems
        do
            for d in $elems
            do
                # for a set of any four elements:
                #   string them together, separated by NUL-bytes
                #   sort them lexicographically ...
                #     ... with NUL separating the elements (-z)
                #     ... and eliminate duplicates (-u)
                #   then replace the NUL bytes with line breaks
                #   allow the shell to split on those line breaks
                #   and chuck the resulting chunks into $1, $2, etc
                set -- $(printf '%s\0' "$a" "$b" "$c" "$d" | sort -z -u | tr "\0" "\n")

                # only if the current selection of elements consisted of four
                # different ones (remember we eliminated duplicates):
                if [ $# -eq 4 ]
                then
                    # create a directory, don't error out if it already exists (-p)
                    mkdir -p "$(printf '%s' "$@")"
                fi
            done
        done
    done
done

非常に効率的ではありませんが、(sort明白な非候補者を呼び出してmkdir同じディレクトリ名を複数回呼び出す場合でも)、内部ループは最大9 4 = 6561の反復を実行し、ワンタイムスクリプトなのでそうではないと思います。最適化に時間を費やす価値があります。


編集:
Xeon E3-1231v3のベンチマーク、いいえmkdir

./elemdirs.sh > /dev/null  11.66s user 1.73s system 173% cpu 7.725 total

そしてそれと一緒に:

./elemdirs.sh > /dev/null  13.80s user 2.16s system 156% cpu 10.215 total

予想数である126個のディレクトリを作成します。コンビネーションここでk = 4、n = 9です。

おすすめ記事