ドッカー内のシェルスクリプト

ドッカー内のシェルスクリプト

Entrypoint.shを使用してdockerを実行しようとしていますが、.sh行は実行されません。

echo `ls -d /input/sub-*/ | sed -e 's/.*sub-\(.*\)\//\1/' | split -l 8 - participants_`

私はdockerfileにこう書いています。

ENTRYPOINT ["bash", "-c", "source /code/entrypoint.sh | ts '[%Y-%m-%d %H:%M:%S]' &>> /output/stderr.log"]

docker run [my_image]でechoが実行されない理由

以下は完全なEntrypoint.shコードです。

#! /bin/bash

alias time='/usr/bin/time -f "%C --- CPU:\t%E real,\t%U user,\t%S 
sys\t%P\tMem:\t%KkiB avg.,\t%MkiB max.\tExit:\t%x"'

echo `ls -d /input/sub-*/ | sed -e 's/.*sub-\(.*\)\//\1/' | split -l 8 - 
participants_`

while read input_bids_path
do      
    participants_id=$(basename $input_bids_path)
    LD_LIBRARY_PATH=/usr/lib/fsl/5.0:$LD_LIBRARY_PATH
    time fmriprep /input /output participant --fs-license-file 
/opt/freesurfer/license.txt --fs-no-reconall --use-aroma --ignore fieldmaps 
--n_cpus 12 --force-bbr --participant_label $(cat $participants_id) -w 
/output
#   rm -r /input/$participants_id
done < <(find /input -name "*participants_*" -type f)
echo  `rm -r /input/$participants_id`
wait `jobs -p` && echo __ok__ || echo __err__

答え1

このスクリプトには、間違っているか改善する可能性がある部分がたくさんあります。

この質問の主な問題は、両方の呼び出しがecho出力を生成しない理由のようです。

コマンド置換を使用する場合、例えば

echo `rm file`

またはそれに対応する

echo $(rm file)

それからechoあなたは得るでしょう出力バックティックや$(...)。コマンド置換では出力は生成されません。バックティック内で使用される両方のコマンドはファイルを変更しますが、標準出力ストリーム(通常は端末に表示されるもの)として出力を生成しません。つまり、echo両方の呼び出しは空行を除いて出力を生成しません。

一般的に言えば、echo $(...)これは同じことをより良い方法で実行できることを意味するアンチパターンです。

もしあなたならするpipeline作成する代わりに、特定のパイプラインの結果を出力したいと思います。

echo $(pipeline)

あなたは単に言うでしょう

pipeline

pipeline通常、コマンド出力が端末に表示されるように、出力が表示されます。

以下のコードでは、printfスクリプトに関連する「進捗情報」を出力するいくつかのステートメントを挿入しました。


これは完全にテストされていない変更されたバージョンのスクリプトですが(使用したツールや入力ファイルにアクセスできないため)しなければならないこれらの中間ファイルの生成を含むスクリプトが実行する作業を模倣します(これらのファイルは必要ありません。後で削除する方法を説明します)。

#!/bin/bash

export LD_LIBRARY_PATH="/usr/lib/fsl/5.0:$LD_LIBRARY_PATH"

timefmt="%C --- CPU:\t%E real,\t%U user,\t%S sys\t%P\tMem:\t%KkiB avg.,\t%MkiB max.\tExit:\t%x"

for dirpath in /input/sub-*/; do
    name=$(basename "$dirpath")
    id=${name#sub-}
    printf '%s\n' "$id"
    printf 'Found ID: %s\n' "$id" >&2
done | split -l 8 - participants_

for participants_id in participants_*; do
    ids=( $(<"$participants_id") )

    printf 'Processing ID: %s\n' "${ids[@]}" >&2

    /usr/bin/time -f "$timefmt" \
    fmriprep /input /output participant \
        --fs-license-file /opt/freesurfer/license.txt \
        --fs-no-reconall --use-aroma \
        --ignore fieldmaps --n_cpus 12 --force-bbr \
        --participant_label "${ids[@]}" \
        -w /output

    rm -f "$participants_id"
done

修理する:

  1. そのオプションに長いオプション引数があるため、コマンドがエイリアスであるtime必要はありません。-fそれにもかかわらず、エイリアスはスクリプトから拡張されません。私はパラメータを文字列として保存して呼び出すときに使用しますtime

  2. LD_LIBRARY_PATHループが次に追加されました。繰り返します。これは必要ありません。

  3. ディレクトリ名からIDを取得するのは、適切なループで行うのが最善です。このループは、後で配列を使用してIDを保存すると消えます。

  4. 中間ファイルの検索に使用する代わりに、単純なfindファイル名のグロービングパターンを使用します。私たちは彼らがそこにいることを知り、彼らの名前も知っています。

  5. 処理されたばかりの中間ファイルはループ内で削除されます。

  6. 連続を使用してコードを読みやすくします。

  7. 通話がwait削除されました。待つバックグラウンドタスクはありません。

all_ids次のバリアントは、一時ファイルの代わりに配列にIDを保存します。

#!/bin/bash

export LD_LIBRARY_PATH="/usr/lib/fsl/5.0:$LD_LIBRARY_PATH"

timefmt="%C --- CPU:\t%E real,\t%U user,\t%S sys\t%P\tMem:\t%KkiB avg.,\t%MkiB max.\tExit:\t%x"

all_ids=( /input/sub-*/ )
all_ids=( "${all_ids[@]#/input/sub-}" ) # remove "/input/sub-" from each item
all_ids=( "${all_ids[@]%/}" )           # remove the trailing "/" from each item

printf 'Found ID: %s\n' "${all_ids[@]}" >&2

n=0
ids=( "${all_ids[@]:0:8}" ) # pick out the first eight IDs

# Loop until the first ID in the ids array is empty
while [ -n "${ids[0]}" ] ; do
    printf 'Processing ID: %s\n' "${ids[@]}" >&2

    /usr/bin/time -f "$timefmt" \
    fmriprep /input /output participant \
        --fs-license-file /opt/freesurfer/license.txt \
        --fs-no-reconall --use-aroma \
        --ignore fieldmaps --n_cpus 12 --force-bbr \
        --participant_label "${ids[@]}" \
        -w /output

    n=$(( n + 1 ))
    ids=( "${all_ids[@]:n*8:8}" ) # pick out the next eight IDs
done

関連情報