简体   繁体   English

bash脚本中变量的转义

[英]An escaping of the variable within bash script

My bash script writes an another bash script using 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

Here there is a problem in escappiong of the elements within date variable 这里的日期变量中的元素的转义有问题

date=\$(date +"\%m_\%d_\%Y")

where the below part did not work properly 以下部分无法正常工作的地方

"\%m_\%d_\%Y"

it results in incomplete of a new bash script produced by printf. 它导致由printf生成的新bash脚本不完整。

How it should be fixed? 应该如何解决?

Thanks! 谢谢!

Use a quoted heredoc. 使用带引号的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"

Note: 注意:

  • Inside a quoted heredoc ( cat <<'EOF' ), no substitutions are performed, so no escaping is needed . 在带引号的heredoc(目录cat <<'EOF' )内, 不会执行任何替换,因此不需要转义 We're thus able to write our code exactly as we want it to exist in the generated file. 因此,我们可以完全按照我们希望其在生成的文件中存在的方式编写代码。
  • When generating code, use printf %q to escape values in such a way as to evaluate back to their original values. 生成代码时,请使用printf %q对值进行转义,以便求回到原始值。 Otherwise, a variable containing $(rm -rf ~) could cause the given command to be run (if it were substituted inside literal single quotes, making the contents $(rm -rf ~)'$(rm -rf ~)' would escape them). 否则,包含$(rm -rf ~)的变量可能会使给定命令运行(如果在文字单引号内将其替换,则使内容$(rm -rf ~)'$(rm -rf ~)'逃脱他们)。
  • Glob expansions return a list of results; Glob扩展返回结果列表; the proper data type in which to store their results is an array, not a string. 存储结果的适当数据类型是数组,而不是字符串。 Thus, file=( gromacs* ) makes the storage of the result in an array explicit, and the following code checks for both the case where we have more than one result, and the case where our result is the original glob expression (meaning no matches existed). 因此, file=( gromacs* )使结果显式存储在数组中,下面的代码将检查是否有多个结果,以及结果是否是原始glob表达式(表示否)。已存在匹配项)。
  • All expansions need to be quoted to prevent string-splitting. 所有扩展都需要加引号,以防止字符串分割。 This means "$HOME"/* , not $HOME/* -- otherwise you'll have problems whenever a user has a home directory containing whitespace (and yes, this does happen -- consider Windows-derived platforms where you have /Users/Firstname Lastname , or sites where you've mounted a volume for home directories off same). 这意味着"$HOME"/* ,而不是$HOME/* -否则,每当用户有一个包含空格的主目录时,您都会遇到问题(是的,这确实会发生–考虑您拥有/Users/Firstname Lastname Windows派生平台/Users/Firstname Lastname ,或者您已在其中装入主目录卷的站点)。
  • pushd and popd are an interactive extension, as opposed to a tool intended for writing scripts. 与用于编写脚本的工具相反, pushdpopd是一个交互式扩展。 Since spawning an external program (such as cp ) involves a fork() operation, and any directory change inside a subshell terminates when that subshell does, you can avoid any need for them by spawning a subshell, cd 'ing within that subshell, and then using the exec builtin to replace the subshell's PID with that of cp , thus preventing the fork that would otherwise have taken place to allow cp to be started in a separate process from the shell acting as its parent. 由于派生外部程序(例如cp )涉及fork()操作,并且在该子Shell发生作用时,该子Shell中的任何目录更改都会终止,因此可以通过产生一个子Shell,在该子Shell中进行cd来避免对它们的任何需要,并且然后使用exec内置execcp替换子shell的PID,从而防止发生fork ,否则将允许cp在与作为其父项的shell分开的进程中启动。

You have to escapes in printf with esscaps, eg: 您必须使用esscaps在printf中转义,例如:

date=\$(date +"%%m_%%d_%%Y")

should print date=\\$(date +"%m_%d_%Y") . 应该打印date=\\$(date +"%m_%d_%Y") But you should avoid using printf , because you don't use it's capabilities. 但是您应该避免使用printf ,因为您没有使用它的功能。 Instead you could cat the string to the file: 相反,您可以将字符串指向文件:

cat > ${output}/collecter.sh <<'END'
HOME=${server}
...
done
END

This would allow you to avoid many escapes, and make the code more readable. 这将使您避免许多转义,并使代码更具可读性。

尝试这个

date=\$(date +\"%%m_%%d_%%Y\")" 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM