循环变量停止工作

循环变量停止工作

我有一个工作 shell 脚本,可以在插入外部 USB 键盘时为其分配不同的布局,但它不再工作。我不知道这是因为我从sh切换到zsh。

usbkbd_id="$(xinput -list | grep "USB Keyboard" | awk -F'=' '{print $2}' | grep "keyboard" | cut -c 1-2)"
for ID in $usbkbd_id; do
    setxkbmap -device ${ID} -layout "es"
done

我注意到,当我用命令检查它时,echo "device: ${ID} layout: ${usbkbd_layout}" >> test.txt它实际上并没有循环遍历每个 ID,而是简单地在整个第一个变量输出的开头添加“device:”...

我已经经历了类似的问题和答案,尝试了我见过的不同替代方案,例如${usbkbd_id[@]},用我的初学者水平脚本编写技能,但我无法解决这个问题。

实际上,也许真正的问题根源在于我总是为我的外部 USB 键盘获取 3 个 ID,而实际上将键盘布局仅分配给其中一个是有效的,但我无法确定是哪一个。所以有时这个命令有效,有时则无效(当正确的 ID 不是我猜的最后一个时)。

答案1

var=value是一个标量赋值。它分配并且只有一个值$var.您可能sh对它也是标量赋值的行为感到困惑,但自从sh,但由于没有列表/数组变量,因此变量的扩展在未引用时会经历特殊的 split+glob 操作(原因是总体而言存在许多错误和安全问题并固定在大多数更现代的 shell 中,包括rcesfishzsh)。

在这里,由于您想要存储多个值,因此您想要使用数组/列表变量,而不是标量变量。

在 中zsh,数组变量赋值是通过以下方式完成的:

var=( values )

句法。

所以:

usbkbd_ids=(
  $(xinput list | grep -Po 'USB Keyboard.*id=\K\d+(?=.*keyboard)')
)
for id in $usbkbd_ids; do
    setxkbmap -device $id -layout es
done

虽然在这里,你不妨这样做:

xinput list |
  grep -Po 'USB Keyboard.*id=\K\d+(?=.*keyboard)' |
  xargs -I ID setxkbmap -device ID -layout es

请注意,您很少需要将awk,grepcut一起使用,它awk是其他两个命令的超集。然而,awk它不太擅长基于模式的提取(除非您可以使用 GNUawk扩展)。上面-P是一个非标准grep扩展。作为标准替代方案,您可以使用sed

  xinput list |
    sed -n 's/^.*USB Keyboard.*id=\([0-9]*\).*keyboard.*/\1/p'

对于标准awk,这可能是:

xinput list |
  awk '/USB Keyboard.*id=.*keyboard/ && match($0, /id=[0-9]+/) {
         print substr($0, RSTART + 3, RLENGTH - 3)
       }'

您还可以使用zsh自己的模式提取功能,例如:

usbkbd_ids=()
for line (${(f)"$(xinput list)"})
  [[ $line =~ 'USB Keyboard.*id=([0-9]+).*keyboard' ]] &&
    usbkbd_ids+=$match[1]

相关内容