$(printf'%q'“ $ {@:1}”)是否等于“ $ {*}”?

时间:2019-06-29 19:41:07

标签: bash shell arguments parameter-passing posix

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

如果是,那么使用纯bash $(printf '%q ' "${@:2}")$*(注意2代替以前的1)是不可能的吗?

相关问题:

  1. POSIX sh equivalent for Bash’s printf %q
  2. How to use printf "%q " in bash?
  3. Bash printf %q invalid directive
  4. How to make runuser correctly forward all command line arguments, instead of trying to interpret them?
  5. How to portability use "${@:2}"?

2 个答案:

答案 0 :(得分:1)

不,它不是等效的,因为单词被拆分了。例如以下代码:

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所示,如果未设置定界符或未将其设置为空格,则$*的输出将被IFS(在此示例中为@)定界。

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

$(printf '%q ' "${@:1}")的输出仍在空间上分割。因此,测试用例2接收3个参数,因为space notspace字符串由空格分隔并分成两个参数。将printf放在"内时无济于事-printf替代ex。 \n个字符的换行符。

案例5678是我尝试使用printf复制"$*"的行为。可以看到在我使用7的情况8eval中,在我引用了命令替换的情况下,在情况56中可以看到。案例(56以及(78的输出应分别与案例14的输出匹配。

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

  

使用纯bash $ *不可能执行$(printf'%q'“ $ {@:2}”)(注意不是2而是以前的1)?

您可能只需要在替换$(shift; printf "%s\n" "$*")内移动参数,但是如上所述,它们始终是不相等的。

答案 1 :(得分:0)

使用@Kamil Cuk作为答案,我建立了这个新的测试代码来进行说明,也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',