对字符串分隔列表执行处理

时间:2021-06-10 09:22:13

标签: bash

我有三组以字符串分隔的变量,如下所示:

listNames="tom~susan~stafano~isabella"
listAges="23~45~34~10"
listNationality="british~american~italian~spanish"

我还有一个要在这些列表中删除的目标变量,即

targetName="susan"

如果目标名称是“susan”,那么我希望在所有变量中删除元素的索引,所以它最终看起来像这样。请注意,我的意思是在较大字符串中某些子字符串的位置上的“索引”,而不是与数组关联的传统意义上的“索引”:

listNames="tom~stafano~isabella"
listAges="23~34~10"
listNationality="british~italian~spanish"

如何在 Bash 中实现它?特别是因为我需要像上面一样以字符串格式保留变量(而不是将它们转换为显式数组结构,因为它们会以上述格式传递到另一个脚本中)。

2 个答案:

答案 0 :(得分:2)

将字符串拆分为数组、删除匹配元素然后重新创建字符串的示例:

#!/usr/bin/env bash

listNames="tom~susan~stafano~isabella"
listAges="23~45~34~10"
listNationality="british~american~italian~spanish"
targetName=susan

# Use tilde instead of whitespace as field delimiter
IFS="~"

# Split into arrays
read -r -a aNames <<<"$listNames"
read -r -a aAges <<<"$listAges"
read -r -a aNationality <<<"$listNationality"

# Look for susan and delete the corresponding elements
nNames=${#aNames[@]}
for (( i = 0; i < nNames; i++)); do
    if [[ ${aNames[i]} == "$targetName" ]]; then
        unset "aNames[i]"
        unset "aAges[i]"
        unset "aNationality[i]"
    fi
done

# Turn back into strings;
# ${foo[*]} puts the value of IFS between elements of foo
listNames=${aNames[*]}
listAges=${aAges[*]}
listNationality=${aNationality[*]}

# Output them for example's sake
declare -p listNames listAges listNationality

答案 1 :(得分:1)

这是另一种没有 @Shawn

使用的 unset 的解决方案
#!/usr/bin/env bash

listNames="tom~susan~stafano~isabella"
listAges="23~45~34~10"
listNationality="british~american~italian~spanish"
targetName="stafano"
to_loop=0

while IFS='~' read -ra line; do
  if ((!to_loop)); then
    for i in "${!line[@]}"; do
      [[ ${line[i]} == "$targetName" ]] &&
      index=$i && break
    done
  fi
  to_loop=1
  line=("${line[*]/"${line[index]}"}")
  line=("${line[*]// /\~}")
  line=("${line[*]/~~/\~}")
  line=("${line[*]%\~}")
  values+=("${line[*]#\~}")
done < <(
  printf '%s\n' "$listNames" "$listAges" "$listNationality"
)

var_names=( listNames listAges listNationality)

##: Just to show the OLD values of the variables not needed in the script
declare -p listNames listAges listNationality; printf '\n'

##: loop through the variable names and assign the new values.
for j in "${!var_names[@]}"; do
  printf -v new_values '%s=%s' "${var_names[j]}" "${values[j]}"
  declare "$new_values"
done

##: Just to show the NEW values of the variables not needed in the script
declare -p listNames listAges listNationality

输出

declare -- listNames="tom~susan~stafano~isabella"
declare -- listAges="23~45~34~10"
declare -- listNationality="british~american~italian~spanish"

declare -- listNames="tom~stafano~isabella"
declare -- listAges="23~34~10"
declare -- listNationality="british~italian~spanish"

使用 unset 这就是我要做的。 (@Shawn 在他的回答中使用)

#!/usr/bin/env bash

listNames="tom~susan~stafano~isabella"
listAges="23~45~34~10"
listNationality="british~american~italian~spanish"
to_loop=0

while IFS='~' read -ra line; do
  if ((!to_loop)); then
    for i in "${!line[@]}"; do
      [[ ${line[i]} == "$targetName" ]] &&
      index=$i && break
    done
  fi
  to_loop=1
  unset 'line[index]'
  values+=("${line[*]}")
done < <(
  printf '%s\n' "$listNames" "$listAges" "$listNationality"
)

var_names=( listNames listAges listNationality )

##: Just to show the OLD values of the variables not needed in the script
declare -p listNames listAges listNationality ; printf '\n'

##: loop through the variable names and assign the new values.
for j in "${!var_names[@]}"; do
  printf -v new_values '%s=%s' "${var_names[j]}" "${values[j]// /\~}"
  declare "$new_values"
done

##: Just to show the NEW values of the variables not needed in the script
declare -p listNames listAges listNationality