我的bash脚本使用printf写了另一个bash脚本。
printf "#!/bin/bash
HOME=${server}
file=gromacs*
file_name=\$(basename "\${file}")
date=\$(date +"\%m_\%d_\%Y")
for sim in \${HOME}/* ; do
if [[ -d \$sim ]]; then
simulation=$(basename "\$sim")
pushd \${sim}
cp \$file \${server}/\${results}/\${file_name}.\${simulation}.\${date}
echo "\${file_name}\ from\ \${simulation}\ has\ been\ collected!"
popd
fi
done" > ${output}/collecter.sh
这里有一个日期变量
中元素的升级问题date=\$(date +"\%m_\%d_\%Y")
以下部分无法正常工作
"\%m_\%d_\%Y"
导致printf生成的新bash脚本不完整。
应如何修复?
谢谢!
答案 0 :(得分:3)
使用引用的heredoc。
{
## print the header, and substitute our own value for HOME
printf '#!/bin/bash\nHOME=%q\n' "$server"
## EVERYTHING BELOW HERE UNTIL THE EOF IS LITERAL
cat <<'EOF'
file=( gromacs* )
(( ${#file[@]} == 1 )) && [[ -e $file ]] || {
echo "ERROR: Exactly one file starting with 'gromacs' should exist" >&2
exit 1
}
file_name=$(basename "${file}")
date=$(date +"%m_%d_%Y")
for sim in "$HOME"/* ; do
if [[ -d $sim ]]; then
simulation=$(basename "$sim")
(cd "${sim}" && exec cp "$file" "${server}/${results}/${file_name}.${simulation}.${date}")
echo "${file_name} from ${simulation} has been collected!"
fi
done
EOF
} >"${output}/collecter.sh"
注意:
cat <<'EOF'
)内,不执行任何替换,因此不需要转义。因此,我们能够完全按照生成的文件中的方式编写代码。printf %q
来转义值,以便评估回原始值。否则,包含$(rm -rf ~)
的变量可能会导致给定要运行的命令(如果它被替换为文字单引号,使内容$(rm -rf ~)'$(rm -rf ~)'
将逃脱它们。)file=( gromacs* )
使结果的存储显式存在,并且以下代码检查两种情况:我们有多个结果,以及我们的结果是原始的glob表达式(意味着没有匹配)的情况。"$HOME"/*
,而不是$HOME/*
- 否则,只要用户拥有问题,您就会遇到问题包含空格的主目录(是的,确实会发生这种情况 - 考虑使用/Users/Firstname Lastname
的Windows派生平台,或者为主目录安装卷的网站。pushd
和popd
是一个交互式扩展,而不是用于编写脚本的工具。自产生外部程序(例如cp
)涉及fork()操作,并且子shell中的任何目录更改在子shell执行时终止,通过在子shell中生成子shell cd
,然后使用子shell,可以避免对它们的任何需要。内置exec
以将子shell的PID替换为cp
的PID,从而阻止了原本允许fork
启用cp
的{{1}}将shell作为其父级的单独进程。答案 1 :(得分:1)
你必须使用esscaps在printf
中逃脱,e。 G:
date=\$(date +"%%m_%%d_%%Y")
应打印date=\$(date +"%m_%d_%Y")
。但是你应该避免使用printf
,因为你没有使用它的功能。相反,你可以将字符串捕捉到文件:
cat > ${output}/collecter.sh <<'END'
HOME=${server}
...
done
END
这将允许您避免许多转义,并使代码更具可读性。
答案 2 :(得分:0)
试试这个
date=\$(date +\"%%m_%%d_%%Y\")"