$(printf '%q ' "${@:1}") 等同于“${*}”吗?

Is $(printf '%q ' "${@:1}") equivalent to "${*}"?

$(printf '%q ' "${@:1}")是否等同于"${*}"

如果是,那么,使用纯 bash $*?

不可能执行 $(printf '%q ' "${@:2}")(注意 2 而不是之前的 1)

相关问题:

  1. POSIX sh equivalent for Bash’s printf %q
  2. Bash printf %q invalid directive

不,这不等价,因为单词被拆分了。前任。以下代码:

check_args() {
  echo "$#=$#"
  printf "%s\n" "$@";
}

# setting arguments
set -- "space notspace" "newline"$'\n'"newline"
echo '1: ---------------- "$*"'
check_args "$*"

echo '2: ---------------- $(printf '\''%q '\'' "${@:1}")'
check_args $(printf '%q ' "${@:1}")

echo '3: ---------------- "$(printf '\''%q '\'' "${@:1}")"'
check_args "$(printf '%q ' "${@:1}")"

echo '4: ---------------- IFS=@ and "$*"'
( IFS=@; check_args "$*"; )

echo "5: ---------------- duplicating quoted"
check_args "$(printf '%s'"${IFS:0:1}" "${@:1}" | sed 's/'"${IFS:0:1}"'$//')"

echo "6: ---------------- duplicating quoted IFS=@"
( IFS=@; check_args "$(printf '%s'"${IFS:0:1}" "${@:1}" | sed 's/'"${IFS:0:1}"'$//')"; )

echo "7: ---------------- duplicating eval unquoted"
eval check_args $(printf '%q"'"${IFS:0:1}"'"' "${@:1}" | sed 's/'"${IFS:0:1}"'$//')

echo "8: ---------------- duplicating eval unquoted IFS=@"
( eval check_args $(IFS=@ ; printf '%q"'"${IFS:0:1}"'"' "${@:1}" | sed 's/"'"${IFS:0:1}"'"$//'); )

将输出:

1: ---------------- "$*"
$#=1
space notspace newline
newline
2: ---------------- $(printf '%q ' "${@:1}")
$#=3
space\
notspace
$'newline\nnewline'
3: ---------------- "$(printf '%q ' "${@:1}")"
$#=1
space\ notspace $'newline\nnewline'
4: ---------------- IFS=@ and "$*"
$#=1
space notspace@newline
newline
5: ---------------- duplicating quoted
$#=1
space notspace newline
newline
6: ---------------- duplicating quoted IFS=@
$#=1
space notspace@newline
newline
7: ---------------- duplicating eval unquoted
$#=1
space notspace newline
newline
8: ---------------- duplicating eval unquoted IFS=@
$#=1
space notspace@newline
newline

repl.

上测试

"$*" 输出由 IFS 分隔的参数。因此,如测试 4 所示,如果分隔符未设置或设置为 space,则 $* 的输出将由 IFS 分隔,在此示例中为 @

此外,当 IFS 未设置或设置为 space 时,$* 的输出不包含终止 space,而 printf '%q ' 将始终打印尾随 space 在字符串的末尾。

$(printf '%q ' "${@:1}") 的输出仍然在 space 上拆分。所以测试用例 2 接收到 3 个参数,因为 space notspace 字符串被 space 分隔并拆分为两个参数。当将 printf 包含在 " 中时,将无济于事 - printf 替代 ex。 \n 个字符换行。

案例 5678 是我尝试使用 printf 复制 "$*" 的行为。可以看出用例78我用了eval,用例56我引用了命令替换。案例(56)和(78)的输出应分别与案例14 的输出相匹配。

为了复制 "$*" 的行为,需要特别注意 IFS 以正确分隔字符串。我使用 sed 's/'"${IFS:0:1}"'$//'printf 输出中删除尾随的 IFS 分隔符。 56 案例是未引用的 $(printf ...) 尝试,6 使用 IFS=@ 来显示分离作品。 78 案例使用对 IFS 进行特殊处理的 eval,因为 IFS 字符本身需要用引号引起来,因此 shell 将不再分裂,这就是为什么 printf '%q"'"${IFS:0:1}"'"'.

doing $(printf '%q ' "${@:2}") (note the 2 instead of 1 as before) is not possible with pure bash $*?

您可能只是将参数转移到替换 $(shift; printf "%s\n" "$*") 中,但如上所示,它们无论如何都不等价。

使用@Kamil Cuk answer as base, I built this new testing code for illustration, also available on repl:

#!/bin/bash
check_args() {
  echo "$#=$#"
  local counter=0
  for var in "$@"
  do
      counter=$((counter+1));
      printf "$counter. '$var', ";
  done
  printf "\n\n"
}

# setting arguments
set -- "space notspace" "lastargument"; counter=1
echo $counter': ---------------- "$*"'; counter=$((counter+1))
check_args "$*"

echo $counter': ---------------- $*'; counter=$((counter+1))
check_args $*

echo $counter': ---------------- "$@"'; counter=$((counter+1))
check_args "$@"

echo $counter': ---------------- $@'; counter=$((counter+1))
check_args $@

echo $counter': ---------------- $(printf '\''%q '\'' "${@:1}")'; counter=$((counter+1))
check_args $(printf '%q ' "${@:1}")

echo $counter': ---------------- "$(printf '\''%q '\'' "${@:1}")"'; counter=$((counter+1))
check_args "$(printf '%q ' "${@:1}")"

echo $counter': ---------------- IFS=@ and "$*"'; counter=$((counter+1))
( IFS=@; check_args "$*"; )

echo "$counter: ---------------- duplicating quoted"; counter=$((counter+1))
check_args "$(printf '%s'"${IFS:0:1}" "${@:1}" | sed 's/'"${IFS:0:1}"'$//')"

echo "$counter: ---------------- duplicating quoted IFS=@"; counter=$((counter+1))
( IFS=@; check_args "$(printf '%s'"${IFS:0:1}" "${@:1}" | sed 's/'"${IFS:0:1}"'$//')"; )

echo "$counter: ---------------- duplicating eval unquoted"; counter=$((counter+1))
eval check_args $(printf '%q"'"${IFS:0:1}"'"' "${@:1}" | sed 's/'"${IFS:0:1}"'$//')

echo "$counter: ---------------- duplicating eval unquoted IFS=@"; counter=$((counter+1))
( eval check_args $(IFS=@ ; printf '%q"'"${IFS:0:1}"'"' "${@:1}" | sed 's/"'"${IFS:0:1}"'"$//'); )

-->

GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)
1: ---------------- "$*"
$#=1
1. 'space notspace lastargument',

2: ---------------- $*
$#=3
1. 'space', 2. 'notspace', 3. 'lastargument',

3: ---------------- "$@"
$#=2
1. 'space notspace', 2. 'lastargument',

4: ---------------- $@
$#=3
1. 'space', 2. 'notspace', 3. 'lastargument',

5: ---------------- $(printf '%q ' "${@:1}")
$#=3
1. 'space', 2. 'notspace', 3. 'lastargument',

6: ---------------- "$(printf '%q ' "${@:1}")"
$#=1
1. 'space\ notspace lastargument ',

7: ---------------- IFS=@ and "$*"
$#=1
1. 'space notspace@lastargument',

8: ---------------- duplicating quoted
$#=1
1. 'space notspace lastargument',

9: ---------------- duplicating quoted IFS=@
$#=1
1. 'space notspace@lastargument',

10: ---------------- duplicating eval unquoted
$#=1
1. 'space notspace lastargument ',

11: ---------------- duplicating eval unquoted IFS=@
$#=1
1. 'space notspace@lastargument',