在bash脚本中转义变量

时间:2017-08-21 16:16:29

标签: bash variables printf

我的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脚本不完整。

应如何修复?

谢谢!

3 个答案:

答案 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"

注意:

  • 在引用的heredoc(cat <<'EOF')内,不执行任何替换,因此不需要转义。因此,我们能够完全按照生成的文件中的方式编写代码。
  • 生成代码时,使用printf %q来转义值,以便评估回原始值。否则,包含$(rm -rf ~)的变量可能会导致给定要运行的命令(如果它被替换为文字单引号,使内容$(rm -rf ~)'$(rm -rf ~)'将逃脱它们。)
  • Glob扩展返回结果列表;存储结果的正确数据类型是数组,而不是字符串。因此,file=( gromacs* )使结果的存储显式存在,并且以下代码检查两种情况:我们有多个结果,以及我们的结果是原始的glob表达式(意味着没有匹配)的情况。
  • 需要引用所有扩展以防止字符串拆分。这意味着"$HOME"/*,而不是$HOME/* - 否则,只要用户拥有问题,您就会遇到问题包含空格的主目录(是的,确实会发生这种情况 - 考虑使用/Users/Firstname Lastname的Windows派生平台,或者为主目录安装卷的网站。
  • pushdpopd是一个交互式扩展,而不是用于编写脚本的工具。自产生外部程序(例如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\")"