【问题标题】:Stripping single and double quotes in a string using bash / standard Linux commands only仅使用 bash / 标准 Linux 命令去除字符串中的单引号和双引号
【发布时间】:2010-10-19 23:19:26
【问题描述】:

我正在寻找仅使用 bash / 标准 Linux 命令来转换字符串的方法:

  1. 应删除围绕字符串的单引号
  2. 应删除围绕字符串的双引号
  3. 未加引号的字符串应保持不变
  4. 带有不匹配引号的字符串应保持不变
  5. 应保留不围绕字符串的单引号
  6. 应该保留不围绕字符串的双引号

例如:

  • “食物”应该变成食物
  • “食物”应该变成食物
  • 食物应该保持不变
  • “食物”应该保持不变
  • “食物”应该保持不变
  • “食物”应该变成食物
  • “食物”应该变成食物
  • 食物应该保持不变
  • 'Fo"od' 应该变成 Fo"od
  • "Fo"od" 应该变成 Fo"od
  • 食物应该保持不变

谢谢!

【问题讨论】:

  • +1 表示所有这些验收测试!
  • 正如大多数“客户”所做的那样,之后添加了一个不在原始集合中的额外 :-)
  • 所以.. 反斜杠转义没有意义? “食物\” -> 食物\ ?

标签: linux bash quotes quoting


【解决方案1】:

应该这样做:

sed "s/^\([\"']\)\(.*\)\1\$/\2/g" in.txt

in.txt 在哪里:

"Fo'od'
'Food'
"Food"
"Fo"od'
Food
'Food"
"Food'
'Fo'od'
"Fo'od"
Fo'od
'Fo"od'
"Fo"od"
Fo"od

而expected.txt 是:

"Fo'od'
Food
Food
"Fo"od'
Food
'Food"
"Food'
Fo'od
Fo'od
Fo'od
Fo"od
Fo"od
Fo"od

您可以检查它们是否匹配:

diff -s <(sed "s/^\([\"']\)\(.*\)\1\$/\2/g" in.txt) expected.txt

【讨论】:

  • 非常接近 :-) echo \"fo\"bar\' | sed "s/([\"'])(.*)\1/\2/g" 得到:fobar' 预期:"fo"bar'
  • 太棒了!编码完成后去改变需求;-) 为这种情况更新。
  • 这是最后一项要求更改 :-) echo \"fo\'bar\' | sed -e "s/([\"'])(.*)\1\$/\2 /g" 得到:"fobar 预期:"fo'bar'
  • 我现在讨厌正则表达式。
  • +1 用于证明“有些人在遇到问题时会认为“我知道,我会使用正则表达式。”现在他们有两个问题。是真实的!
【解决方案2】:

你可以使用tr:

echo "$string" | tr -d 'chars to delete' 

... 也可以使用,但是已知 'tr' 在更旧的(大约 Redhat 9-ish)发行版上存在问题。 tr 是 'translate' 的缩写,常用于管道转换输入。 -d 选项仅表示“删除”。

大多数现代版本还包含预定义的宏,用于将上转换为下、下转换为上、消除空白等。因此,如果您使用它,请花点时间了解它还有什么作用(请参阅帮助输出/man页),派上用场。

【讨论】:

    【解决方案3】:
    VAR="'FOOD'"
    
    VAR=$(eval echo $VAR)
    

    说明:由于 shell 已经理解引号,因此您可以要求 shell 评估仅回显带引号的字符串的命令,就像您自己键入时一样。

    这里,eval echo $VAR 扩展为 eval echo 'FOOD',因为引号实际上是 VAR 值的一部分。如果你在 shell 中运行echo 'FOOD',你会得到FOOD(不带引号)。这就是 eval 所做的:它接受输入并像 shell 命令一样运行它。

    ⚠代码注入!

    eval 将脚本暴露给代码注入。

    VAR=';ls -l'
    
    VAR=$(eval echo $VAR)
    

    将导致执行ls -l

    这里可能会注入更多有害代码。

    【讨论】:

    • 请多加解释!!
    • 我添加了一些解释并对示例进行了一些现代化改造。
    【解决方案4】:

    您可能想使用 sed...

    echo $mystring | sed -s "s/^\(\(\"\(.*\)\"\)\|\('\(.*\)'\)\)\$/\\3\\5/g"
    

    【讨论】:

    • 我不知道它是否有效,但是为您的 sed 命令的真棒 +1 :)
    • 这非常接近,但仅差一点点。 echo \"fo\"bar\' | sed -s "s/^(\"(.*)\")\|(\'(.*)\')\$/\\2/g" 得到:fobar' 预期:"fo"bar'
    • 是的......由于某种原因我无法理解,最后一个 $ 顽固地拒绝匹配我的字符串的结尾。正在努力...
    • 另外,引用你的参数扩展。
    【解决方案5】:

    仅使用 Bash 内置函数(即 Bash 参数扩展):

    IFS=' ' 
    
    food_strings=( "'Food'" '"Food"' Food "'Food\"" "\"Food'" "'Fo'od'" "\"Fo'od\"" "Fo'od" "'Fo\"od'" '"Fo"od"' 'Fo"od'  )  
    
    for food in ${food_strings[@]}; do 
    
       [[ "${food#\'}" != "$food" ]] && [[ "${food%\'}" != "$food" ]] && { food="${food#\'}"; food="${food%\'}"; } 
    
       [[ "${food#\"}" != "$food" ]] && [[ "${food%\"}" != "$food" ]] && { food="${food#\"}"; food="${food%\"}"; } 
    
       echo "$food"
    
    done 
    

    有关 Bash 参数扩展的另一个示例,请参见:

    http://codesnippets.joyent.com/posts/show/1816

    【讨论】:

      【解决方案6】:

      也偶然发现了这一点。对于前三个测试用例,eval echo $string 运行良好。为了让它适用于所有请求的案例和其他一些案例,我想出了这个(用 bashdash 测试):

      #!/bin/sh
      
      stripquotes() {
          local firstchar="`substr "$1" 0 1`"
          local len=${#1}
          local ilast=$((${#1} - 1))
          local lastchar="`substr "$1" $(($len - 1))`"
          if [ "$firstchar" = '"' ] || [ "$firstchar" = "'" ] && [ $firstchar = $lastchar ]; then
              echo "`substr "$1" 1 $(($len - 2))`"
          else
              echo "$1"
          fi
      }
      
      # $1 = String.
      # $2 = Start index.
      # $3 = Length (optional). If unspecified or an empty string, the length of the
      #      rest of the string is used.
      substr() {
          local "len=$3"
          [ "$len" = '' ] && len=${#1}
          if ! (echo ${1:$2:$len}) 2>/dev/null; then
              echo "$1" | awk "{ print(substr(\$0, $(($2 + 1)), $len)) }"
          fi
      }
      
      var="'Food'"
      stripquotes "$var"
      
      var='"Food"'
      stripquotes "$var"
      
      var=Food
      stripquotes "$var"
      
      var=\'Food\"
      stripquotes "$var"
      
      var=\"Food\'
      stripquotes "$var"
      
      var="'Fo'od'"
      stripquotes "$var"
      
      var="\"Fo'od\""
      stripquotes "$var"
      
      var="Fo'od"
      stripquotes "$var"
      
      var="'Fo\"od'"
      stripquotes "$var"
      
      var="\"Fo\"od\""
      stripquotes "$var"
      
      var="Fo\"od"
      stripquotes "$var"
      
      # A string with whitespace should work too.
      var="'F\"o 'o o o' o\"d'"
      stripquotes "$var"
      
      # Strings that start and end with the same character that isn't a quote or
      # doublequote should stay the same.
      var="TEST"
      stripquotes "$var"
      
      # An empty string should not cause errors.
      var=
      stripquotes "$var"
      
      # Strings of length 2 that begin and end with a quote or doublequote should not
      # cause errors.
      var="''"
      stripquotes "$var"
      var='""'
      stripquotes "$var"
      

      【讨论】:

        【解决方案7】:
        python -c "import sys;a=sys.stdin.read();a=a.strip();print (a[1:-1] if a[0]==a[-1] and a[0] in \"'\\\"\" else a)"
        

        它不能很好地处理边缘情况(例如空字符串),但它将作为一个起点。如果它们相同并且如果它们是 ' 或 "

        【讨论】:

        • Jason 明确表示他想要 bash / 标准 Linux 命令。尽管大多数常用发行版都附带了 python,但它肯定不是标准的。例如,如果你安装一个基本的 Debian 系统,它不包括 python。
        猜你喜欢
        • 2013-12-05
        • 1970-01-01
        • 2021-02-26
        • 2020-04-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-13
        相关资源
        最近更新 更多