【问题标题】:Why does piping out to /dev/null and grep, pollute my bash array?为什么管道输出到 /dev/null 和 grep 会污染我的 bash 数组?
【发布时间】:2020-05-25 06:20:07
【问题描述】:

我有两个 shell 脚本(Linux,#!/bin/bash),一个有一些常用的功能来查找文本等,另一个利用常用的功能做一些“实际”的工作。

在检查数组变量(test2.sh 内的oranges)以测试/查看它是否是一个数组(declare -p oranges 2>/dev/null | grep -q '^declare \-a' && printf "$fmt" "oranges is an indexed array")时,我偶然发现另一个数组(LINEStest1.sh)突然得到带有随机数的“污染”。

我不明白为什么会这样。有人有答案吗? 如果您在使用脚本时使用 echo 或 printf ,则只有当您将错误传递到 2>/dev/null 并 grep 输出时,它才会产生零差异。我已将脚本缩减为基本要素以显示问题。 此外,通过将单行分开并将每个部分的输出分配给一个变量(参见test3.sh),这个问题很容易解决,所以我不会被困在脚本编写或进一步的进展上。我一直试图回答为什么会发生LINES 的污染。

test1.sh

#!/bin/bash

declare -a LINES

echo "::: DECLARE ::: Length of [LINES] is: ${#LINES[@]}, :::Content of [LINES] is: ${LINES[@]}"

test2.sh

#!/bin/bash

source ./test1.sh

declare -a oranges=()
echo "::: test2-1 ::: Length of [LINES] is: ${#LINES[@]}, :::Content of [LINES] is: ${LINES[@]}"


declare -p oranges 2>/dev/null
echo "::: test2-2 ::: Length of [LINES] is: ${#LINES[@]}, :::Content of [LINES] is: ${LINES[@]}"


declare -p oranges 2>/dev/null | grep -E -i '^declare \-a'
echo "::: test2-3 ::: Length of [LINES] is: ${#LINES[@]}, :::Content of [LINES] is: ${LINES[@]}"


declare -p oranges 2>/dev/null | grep -E -i '^declare \-a' && printf "$fmt" "oranges is an indexed array"
echo "::: test2-4 ::: Length of [LINES] is: ${#LINES[@]}, :::Content of [LINES] is: ${LINES[@]}"

declare -p LINES
declare -p oranges 2>/dev/null | grep -E -i '^declare \-a' && printf "$fmt" "oranges is an indexed array" || printf "$fmt" "oranges is not an indexed array"
echo "::: test2-5 ::: Length of [LINES] is: ${#LINES[@]}, :::Content of [LINES] is: ${LINES[@]}"


declare -p oranges 2>/dev/null
echo "::: test2-6 ::: Length of [LINES] is: ${#LINES[@]}, :::Content of [LINES] is: ${LINES[@]}"

并且在运行时会出现以下输出:

::: DECLARE ::: Length of [LINES] is: 0, :::Content of [LINES] is: 
::: test2-1 ::: Length of [LINES] is: 0, :::Content of [LINES] is: 
declare -a oranges=()
::: test2-2 ::: Length of [LINES] is: 0, :::Content of [LINES] is: 
declare -a oranges=()
::: test2-3 ::: Length of [LINES] is: 1, :::Content of [LINES] is: 81
declare -a oranges=()
::: test2-4 ::: Length of [LINES] is: 1, :::Content of [LINES] is: 81
declare -a LINES=([0]="81")
declare -a oranges=()
::: test2-5 ::: Length of [LINES] is: 1, :::Content of [LINES] is: 81
declare -a oranges=()
::: test2-6 ::: Length of [LINES] is: 1, :::Content of [LINES] is: 81

test3.sh - 解决方法

#!/bin/bash

source ./test1.sh

declare -a oranges=()
echo "::: test2-1 ::: Length of [LINES] is: ${#LINES[@]}, :::Content of [LINES] is: ${LINES[@]}"


someResult="$(declare -p oranges 2>/dev/null)"
thePattern="^declare \-a"

nextResult=`grep -E -i "$thePattern" <<< "$someResult"` || nextResult=""

if [[ -n "$nextResult" ]]
then
    echo "oranges is an indexed array"
else
    echo "oranges is NOT an indexed array"
fi

echo "::: test2-2 ::: Length of [LINES] is: ${#LINES[@]}, :::Content of [LINES] is: ${LINES[@]}"

test3.sh 的输出 ..

$ ./test3.sh 
::: DECLARE ::: Length of [LINES] is: 0, :::Content of [LINES] is: 
::: test2-1 ::: Length of [LINES] is: 0, :::Content of [LINES] is: 
oranges is an indexed array
::: test2-2 ::: Length of [LINES] is: 0, :::Content of [LINES] is: 

【问题讨论】:

  • 我注意到在您奇怪的输出(“错误”情况)中,我们从未看到文本 _oranges 是一个索引数组_,尽管这应该被打印出来,不是吗?

标签: arrays linux bash shell


【解决方案1】:

LINES 是由 shell 填充的保留变量,其中包含终端窗口中可以容纳的行数。使用不同的变量名(shell 不使用该变量名;请参阅Shell Variables),它会按预期工作。

正如 David C. Rankin 在他的评论中提到的,所有 UPPER-CASE 中的变量通常都是为 shell 保留的,这就是为什么建议使用小写变量名的原因。

如果您出于某种原因必须为变量使用名称 LINES,您可以通过以下命令禁用 checkwinsize 选项作为解决方法;但这可能会产生副作用,我不能保证它会一直有效。

shopt -u checkwinsize

【讨论】:

  • 值得一提的是,所有 UPPER-CASE 中的变量通常都是为 shell 保留的,这就是为什么建议使用小写变量名的原因......可能想让他试试 echo $LINESecho $COLUMNS 来自 xterm 以了解为什么存在冲突 :)
  • @user1934428 不是未设置,而是给定了数组属性(试试foo=1; declare -a foo; echo ${foo[@]})。我不太了解 bash 的内部结构,但是您可以清楚地看到,当输出是 TTY 并且通过比较 bash -c 'LINES=; :; echo $LINES'bash -c 'LINES=; : | :; echo $LINES' 来执行具有多个组件的管道时,它会更新 LINES 和 COLUMNS 变量。这是 bash 5.0.11 顺便说一句。
  • @oguzismail 在 man bash 中,LINESCOLUMNS 都在解释性段落 "The following variables are used by the shell. ..." 下,在 LINES 的情况下是 "Automatically set if the checkwinsize option is enabled or in an interactive shell..." 我将不得不进一步潜水 - - 但被 shell 使用意味着它可以随时使用 - 我怀疑这会导致问题中的覆盖行为。
  • @oguzismail : 也许我会联系终端应用程序的实现者。但是,tput lines 确实产生了正确的数字,因此 bash 使用的机制可能与 tput 不同。
  • @oguzismail 不错!非常感谢!我忙于测试“为什么”的不同场景,以至于错过了其他因素,例如 Shell 保留变量。并为大家花时间深入研究问题并回复回复而欢呼。我更新了我的数组变量名,一切都很好。
【解决方案2】:

这可以解释这种行为:

d。非交互式 shell 现在对 checkwinsize 的设置作出反应 并设置 前台作业退出后的行和列。

来自http://www.softpanorama.org/Scripting/Shellorama/Bash_history/bash43.shtml

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-01
    • 2019-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多