首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从sh内部生成sh代码:转义

从sh内部生成sh代码:转义
EN

Stack Overflow用户
提问于 2012-11-13 01:11:40
回答 2查看 1.2K关注 0票数 2

我有一个shell变量(我们将调用x),它包含一个带有shell元字符的字符串。例如,我可能

代码语言:javascript
复制
abc  "def's"  ghi

由.

代码语言:javascript
复制
x='abc  "def'\''s"  ghi'

我希望从该字符串构建一个shell命令(存储在文件中,而不是执行)。我有什么选择?

代码语言:javascript
复制
echo "prog $x"     >>file     # Doesn't work
echo "prog '$x'"   >>file     # Doesn't work
echo "prog \"$x\"" >>file     # Doesn't work

当前解决方案使用sed

代码语言:javascript
复制
y=`echo "$x" | sed 's/\([^a-zA-Z0-9._\-\/]\)/\\\\\1/g'`
echo "prog $y" >>file

产出如下(尽管同等产出也是可以接受的):

代码语言:javascript
复制
prog abc\ \ \"def\'s\"\ \ ghi

问题是,需要这样做的地方越来越多。有人有更好的解决方案吗?

备注:

  • 必须适用于sh (相对于bash)和任何sh可能合理别名的东西(例如,在sh仿真模式下的bash )。
  • 它必须尽可能可移植(不包括Windows)。
  • perl的使用是不可接受的,但其他unix工具的使用可能随处可见。
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-09-06 06:42:49

下面的解决方案将适用于Bourne可以处理的所有输入字符串(包括换行符),在大多数系统上不使用外部命令,并且可移植到所有现代的类似Bourne的shell :放入此esceval函数的每个参数都分别正确地转义/引用。(我把它命名为esceval,意思是“逃逸评估”,以防您有疑问。)

代码语言:javascript
复制
esceval()
{
    case $# in 0) return 0; esac
    while :
    do
        printf "'"
        unescaped=$1
        while :
        do
            case $unescaped in
            *\'*)
                printf %s "${unescaped%%\'*}""'\''"
                unescaped=${unescaped#*\'}
            ;;
            *)
                printf %s "$unescaped"
                break
            ;;
            esac
        done
        shift
        case $# in 0) break; esac
        printf "' "
    done
    printf "'\n"
}

以上对我的保证作了更多迂腐的阐述:

  1. 上面的代码可以移植到所有具有功能的shell(现在实际使用的所有shell)以及${foo#bar}${foo%%bar}替换(除非您正在获取Solaris 10的/bin/sh或类似的古物,否则您需要关注的所有shell)。
  2. 上面的代码不需要分叉/执行任何新的进程,除非printf不是shell中的内置程序( printf仅作为外部命令可用是相当少见的,而sed几乎总是外部命令,而且我已经看到了更多的精简系统,这些系统有printf,但没有sed,如果这很重要的话)。

注意:此处发布的版本将一个变量泄漏到全局命名空间(unescaped)中,但您可以通过声明local unescaped (如果您的shell支持该变量)或在子shell中包装函数体(括号--它们甚至可以替换花括号)来轻松地修复这个问题,尽管如果您使用该路径,这一点在视觉上并不明显,而且大多数shell都会为子shell进行分叉处理)。

另一方面,如果您确实需要支持没有这些变量子字符串替换的系统,那么您可以使用sed,但是您需要小心地正确地转义字符串中的换行符这样的棘手的事情:

代码语言:javascript
复制
esceval()
{
    case $# in 0) return 0; esac
    while :
    do
        printf "'"
        printf %s "$1" | sed "s/'/'\\\\''/g"
        shift
        case $# in 0) break; esac
        printf "' "
    done
    printf "'\n"
}

关于尾随换行符的最后注意事项:您应该注意到Bourne shells从命令替换中剥离了尾行(大多数行都带尾换行符,少数shell只带1行)。换句话说,对此感到厌倦:

代码语言:javascript
复制
# Literal strings work fine:
esceval 'foo



'

# Quoted variable substitution also works fine:
ln='
'
esceval "foo$ln$ln$ln$ln"

# Breaks - newlines never make it into `esceval`:
esceval "`printf 'foo\n\n\n\n'`"

还有我做的这个怪物,它将在前两个版本之间进行选择,这取决于您的shell是否有能力支持必要的变量替换语法(但是,它看起来很糟糕,因为只有shell的版本必须在一个eval-ed字符串中,这样才能防止不兼容的shell在他们看到它的时候崩溃):https://github.com/mentalisttraceur/esceval/blob/master/sh/esceval.sh

票数 2
EN

Stack Overflow用户

发布于 2012-11-13 01:35:33

sh具有功能。

代码语言:javascript
复制
# to_shell_lit() - Creates a shell literal
# Usage:  printf '%s\n' "...$( quote "..." )..."
to_shell_lit() {
    printf \'
    printf %s "$1" | sed "s/'/'\\\\''/g"
    printf \'
}

测试:

代码语言:javascript
复制
$ x='abc  "def'\''s"  ghi'

$ printf '%s\n' "$x"
abc  "def's"  ghi

$ printf '%s\n' "prog `to_shell_lit "$x"`"
prog 'abc  "def'\''s"  ghi'

$ printf '%s\n' "prog $( to_shell_lit "`pwd`" )"
prog '/home/ikegami/foo bar'
代码语言:javascript
复制
$ printf '%s\n' "$( to_shell_lit "a'b" )"
'a'\''b'

$ printf '%s\n' "$( to_shell_lit '-n' )"
'-n'

$ printf '%s\n' "$( to_shell_lit '\\' )"
'\\'

$ printf '%s\n' "$( to_shell_lit 'foo
bar' )"
'foo
bar'

采用多个参数的版本:

代码语言:javascript
复制
# to_shell_lit() - Creates a shell literal
# Usage:  printf '%s\n' "$( to_shell_lit "..." "..." "..." )"
to_shell_lit() {
    local prefix=''
    local p
    for p in "$@" ; do
        printf "$prefix"\'
        printf %s "$p" | sed "s/'/'\\\\''/g"
        printf \'
        prefix=' '
    done
}
票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/13354247

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档