docker内的shell脚本

docker内的shell脚本

我尝试使用 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"]

为什么 echo 不在 docker run [my_image] 内执行

这是完整的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_PATHin每个迭代。这是不需要的。

  3. 从目录名称获取 ID 最好在适当的循环中完成。当我们使用数组来存储 ID 时,这个循环稍后就会消失。

  4. 我们不使用它们find来定位中间文件,而是将它们与简单的文件名通配模式一起使用。我们知道他们就在那里,也知道他们叫什么名字。

  5. 刚刚处理的中间文件在循环内被删除。

  6. 通过使用续行使代码变得可读。

  7. 的呼叫wait已被删除。没有需要等待的后台任务。

以下变体将 ID 存储在数组中,all_ids而不是临时文件中:

#!/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

相关内容