Unix“alias”在使用“awk”命令时失败

时间:2021-05-26 16:03:52

I'm creating an alias in Unix and have found that the following command fails..

我正在Unix中创建一个别名,并发现以下命令失败。

alias logspace='find /apps/ /opt/ -type f -size +100M -exec ls -lh {} \; | awk '{print $5, $9 }''

I get the following :

我得到以下信息:

awk: cmd. line:1: {print
awk: cmd. line:1:       ^ unexpected newline or end of string

Any ideas on why the piped awk command fails...

关于为什么piped awk命令失败的任何想法…

Thanks, Shaun.

谢谢你,肖恩。

3 个解决方案

#1


30  

To complement's @Dropout's helpful answer:

补充@Dropout的有益回答:

tl;dr

The problem is the OP's attempt to use ' inside a '-enclosed (single-quoted) string.

问题是OP试图使用“inside”—封闭(单引号)字符串。

The most robust solution in this case is to replace each interior ' with '\'' (sic):

在这种情况下,最可靠的解决方案是用“\”(sic)替换每个内部结构:

alias logspace='find /apps/ /opt/ -type f -size +100M -exec ls -lh {} \; | 
                awk '\''{print $5, $9 }'\'''
  • Bourne-like (POSIX-compatible) shells do not support using ' chars inside single-quoted ('...'-enclosed) strings AT ALL - not even with escaping.
    • (By contrast, you CAN escape " inside a double-quoted string as \", and, as in @Droput's answer, you can directly, embed ' chars. there, but see below for pitfalls.)
    • (相比之下,您可以“在一个双引号的字符串中转义为\”,正如@Droput的答案一样,您可以直接嵌入‘chars’。在那里,但看看下面的陷阱。
  • 类bourne(与posix兼容)shell根本不支持在单引号括起来的字符串中使用“chars”——甚至连转义都不支持。(相比之下,您可以“在一个双引号的字符串中转义为\”,正如@Droput的答案一样,您可以直接嵌入‘chars’。在那里,但看看下面的陷阱。
  • The solution above effectively builds the string from multiple, single-quoted strings into which literal ' chars. - escaped outside the single-quoted strings as \' - are spliced in.
    Another way of putting it, as @Etan Reisinger has done in a comment: '\'' means: "close string", "escape single quote", "start new string".
  • 上面的解决方案有效地将字符串从多个单引号的字符串构建为文字字符。-在单引号外转义为\' -被拼接进去。换句话说,正如@Etan Reisinger在评论中所做的:'\ "的意思是:"close string", "escape single quote", "start new string"。
  • When defining an alias, you usually want single quotes around its definition so as to delay evaluation of the command until the alias is invoked.
  • 在定义别名时,通常需要在其定义周围使用单引号,以便在调用别名之前延迟对命令的计算。

Other solutions and their pitfalls:

其他解决方案及其缺陷:

The following discusses alternative solutions, based on the following alias:

下面根据以下别名讨论其他解决方案:

alias foo='echo A '\''*'\'' is born at $(date)'

Note how the * is effectively enclosed in single quotes - using above technique - so as to prevent pathname expansion when the alias is invoked later.

注意*如何有效地包含在单引号中—使用上述技术—以便在稍后调用别名时防止路径名扩展。

When invoked, this alias prints literal A * star is born, followed by the then-current date and time, e.g.: A * is born at Mon Jun 16 11:33:19 EDT 2014.

当被调用时,这个别名打印出一个* * * * *诞生,然后是当时的日期和时间,例如:A *诞生于2014年东部时间11月16日11:33:33 - 19。


Use a feature called ANSI C quoting with shells that support it: bash, ksh, zsh

使用一个名为ANSI C的特性,引用支持它的shell: bash、ksh、zsh。

ANSI C-quoted strings, which are enclosed in $'...', DO allow escaping embedded ' chars. as \':

ANSI c -引号字符串,包含在$'…,请允许隐藏的字符。正如\ ':

alias foo=$'echo A \'*\' is born at $(date)'

Pitfalls:

陷阱:

  • This feature is not part of POSIX.
  • 这个特性不是POSIX的一部分。
  • By design, escape sequences such as \n, \t, ... are interpreted, too (in fact, that's the purpose of the feature).
  • 通过设计,转义序列如\n、\t、……也被解释(事实上,这就是特性的目的)。

Use of alternating quoting styles, as in @Dropout's answer:

使用交替引用样式,如@Dropout的回答:

Pitfall:

缺陷:

'...' and "..." have different semantics, so substituting one for the other can have unintended side-effects:

“…和“…”有不同的语义,所以用一个替代另一个可能产生意想不到的副作用:

alias foo="echo A '*' is born at $(date)" # DOES NOT WORK AS INTENDED

While syntactically correct, this will NOT work as intended, because the use of double quotes causes the shell to expand the command substitution $(date) right away, and thus hardwires the date and time at the time of the alias definition into the alias.

虽然语法上是正确的,但这并不能正常工作,因为使用双引号会导致shell立即展开命令替换$(date),从而将别名定义时的日期和时间硬连到别名中。

As stated: When defining an alias, you usually want single quotes around its definition so as to delay evaluation of the command until the alias is invoked.

如前所述:在定义别名时,您通常需要围绕其定义进行单引号,以便在调用别名之前延迟对该命令的评估。


Finally, a caveat:

最后,一个警告:

The tricky thing in a Bourne-like shell environment is that embedding ' inside a single-quoted string sometimes - falsely - APPEARS to work (instead of generating a syntax error, as in the question), when it instead does something different:

在一个类似bournei的shell环境中,复杂的事情是嵌入“在一个单引号的字符串中,有时是错误的”(而不是生成语法错误,就像在这个问题中一样),当它做了一些不同的事情:

 alias foo='echo a '*' is born at $(date)'  # DOES NOT WORK AS EXPECTED.

This definition is accepted (no syntax error), but won't work as expected - the right-hand side of the definition is effectively parsed as 3 strings - 'echo a ', *, and ' is born at $(date)', which, due to how the shell parses string (merging adjacent strings, quote removal), results in the following, single, literal string: a * is born at $(date). Since the * is unquoted in the resulting alias definition, it will expand to a list of all file/directory names in the current directory (pathname expansion).

这个定义被接受(没有语法错误),但不会像预期的那样工作,右手边的定义是有效解析字符串3——“回声”,*,和出生在(日期)美元,,由于壳牌如何解析字符串(合并相邻的字符串,引用删除),结果在以下,单身,文字字符串:*出生在(日期)美元。由于在产生的别名定义中没有引用*,因此它将扩展到当前目录中所有文件/目录名的列表(路径名扩展)。

#2


3  

You chould use different quotes for surrounding the whole text and for inner strings.

对于整个文本和内部字符串,应该使用不同的引号。

Try changing it to

试着改变它

alias logspace="find /apps/ /opt/ -type f -size +100M -exec ls -lh {} \; | awk '{print $5, $9 }'"

In other words, your outer quotes should be different than the inner ones, so they don't mix.

换句话说,你的外部引语应该与内部引语不同,这样它们就不会混合。

#3


2  

Community wiki update:

社区wiki更新:

  • The redeeming feature of this answer is recognizing that the OP's problem lies in unescaped use of the string delimiters (') inside a string.
  • 这个答案的可取之处是识别OP的问题在于字符串分隔符(')的不可转义使用。
  • However, this answer contains general string-handling truths, but does NOT apply to (Bourne-like, POSIX-compatible) shell programming specifically, and thus does not address the OP's problem directly - see the comments.
  • 但是,这个答案包含了一般的字符串处理真理,但是不适用于(类似bourne, posix兼容的)shell编程,因此不能直接解决OP的问题——请参阅注释。

Note: Code snippets are meant to be pseudo code, not shell language.

注意:代码片段是伪代码,而不是shell语言。

Basic strings: You canNOT use the same quote within the string as the entire string is delimited with:

基本字符串:您不能在字符串中使用相同的引号,因为整个字符串是用:

foo='hello, 'world!'';
    ^--start string
            ^-- end string
             ^^^^^^^^--- unknown "garbage" causing syntax error.

You have to escape the internal strings:

你必须摆脱内部的束缚:

foo='hello, \'world!\'';
            ^--escape

This is true of pretty much EVERY programming language on the planet. If they don't provide escaping mechanisms, such as \, then you have to use alternate means, e.g.

这几乎适用于地球上的每一种编程语言。如果它们不提供转义机制,例如\,那么您必须使用替代方法,例如。

quotechar=chr(39); // single quote is ascii #39
foo='hello ,' & quotechar & 'world!' & quotechar;

#1


30  

To complement's @Dropout's helpful answer:

补充@Dropout的有益回答:

tl;dr

The problem is the OP's attempt to use ' inside a '-enclosed (single-quoted) string.

问题是OP试图使用“inside”—封闭(单引号)字符串。

The most robust solution in this case is to replace each interior ' with '\'' (sic):

在这种情况下,最可靠的解决方案是用“\”(sic)替换每个内部结构:

alias logspace='find /apps/ /opt/ -type f -size +100M -exec ls -lh {} \; | 
                awk '\''{print $5, $9 }'\'''
  • Bourne-like (POSIX-compatible) shells do not support using ' chars inside single-quoted ('...'-enclosed) strings AT ALL - not even with escaping.
    • (By contrast, you CAN escape " inside a double-quoted string as \", and, as in @Droput's answer, you can directly, embed ' chars. there, but see below for pitfalls.)
    • (相比之下,您可以“在一个双引号的字符串中转义为\”,正如@Droput的答案一样,您可以直接嵌入‘chars’。在那里,但看看下面的陷阱。
  • 类bourne(与posix兼容)shell根本不支持在单引号括起来的字符串中使用“chars”——甚至连转义都不支持。(相比之下,您可以“在一个双引号的字符串中转义为\”,正如@Droput的答案一样,您可以直接嵌入‘chars’。在那里,但看看下面的陷阱。
  • The solution above effectively builds the string from multiple, single-quoted strings into which literal ' chars. - escaped outside the single-quoted strings as \' - are spliced in.
    Another way of putting it, as @Etan Reisinger has done in a comment: '\'' means: "close string", "escape single quote", "start new string".
  • 上面的解决方案有效地将字符串从多个单引号的字符串构建为文字字符。-在单引号外转义为\' -被拼接进去。换句话说,正如@Etan Reisinger在评论中所做的:'\ "的意思是:"close string", "escape single quote", "start new string"。
  • When defining an alias, you usually want single quotes around its definition so as to delay evaluation of the command until the alias is invoked.
  • 在定义别名时,通常需要在其定义周围使用单引号,以便在调用别名之前延迟对命令的计算。

Other solutions and their pitfalls:

其他解决方案及其缺陷:

The following discusses alternative solutions, based on the following alias:

下面根据以下别名讨论其他解决方案:

alias foo='echo A '\''*'\'' is born at $(date)'

Note how the * is effectively enclosed in single quotes - using above technique - so as to prevent pathname expansion when the alias is invoked later.

注意*如何有效地包含在单引号中—使用上述技术—以便在稍后调用别名时防止路径名扩展。

When invoked, this alias prints literal A * star is born, followed by the then-current date and time, e.g.: A * is born at Mon Jun 16 11:33:19 EDT 2014.

当被调用时,这个别名打印出一个* * * * *诞生,然后是当时的日期和时间,例如:A *诞生于2014年东部时间11月16日11:33:33 - 19。


Use a feature called ANSI C quoting with shells that support it: bash, ksh, zsh

使用一个名为ANSI C的特性,引用支持它的shell: bash、ksh、zsh。

ANSI C-quoted strings, which are enclosed in $'...', DO allow escaping embedded ' chars. as \':

ANSI c -引号字符串,包含在$'…,请允许隐藏的字符。正如\ ':

alias foo=$'echo A \'*\' is born at $(date)'

Pitfalls:

陷阱:

  • This feature is not part of POSIX.
  • 这个特性不是POSIX的一部分。
  • By design, escape sequences such as \n, \t, ... are interpreted, too (in fact, that's the purpose of the feature).
  • 通过设计,转义序列如\n、\t、……也被解释(事实上,这就是特性的目的)。

Use of alternating quoting styles, as in @Dropout's answer:

使用交替引用样式,如@Dropout的回答:

Pitfall:

缺陷:

'...' and "..." have different semantics, so substituting one for the other can have unintended side-effects:

“…和“…”有不同的语义,所以用一个替代另一个可能产生意想不到的副作用:

alias foo="echo A '*' is born at $(date)" # DOES NOT WORK AS INTENDED

While syntactically correct, this will NOT work as intended, because the use of double quotes causes the shell to expand the command substitution $(date) right away, and thus hardwires the date and time at the time of the alias definition into the alias.

虽然语法上是正确的,但这并不能正常工作,因为使用双引号会导致shell立即展开命令替换$(date),从而将别名定义时的日期和时间硬连到别名中。

As stated: When defining an alias, you usually want single quotes around its definition so as to delay evaluation of the command until the alias is invoked.

如前所述:在定义别名时,您通常需要围绕其定义进行单引号,以便在调用别名之前延迟对该命令的评估。


Finally, a caveat:

最后,一个警告:

The tricky thing in a Bourne-like shell environment is that embedding ' inside a single-quoted string sometimes - falsely - APPEARS to work (instead of generating a syntax error, as in the question), when it instead does something different:

在一个类似bournei的shell环境中,复杂的事情是嵌入“在一个单引号的字符串中,有时是错误的”(而不是生成语法错误,就像在这个问题中一样),当它做了一些不同的事情:

 alias foo='echo a '*' is born at $(date)'  # DOES NOT WORK AS EXPECTED.

This definition is accepted (no syntax error), but won't work as expected - the right-hand side of the definition is effectively parsed as 3 strings - 'echo a ', *, and ' is born at $(date)', which, due to how the shell parses string (merging adjacent strings, quote removal), results in the following, single, literal string: a * is born at $(date). Since the * is unquoted in the resulting alias definition, it will expand to a list of all file/directory names in the current directory (pathname expansion).

这个定义被接受(没有语法错误),但不会像预期的那样工作,右手边的定义是有效解析字符串3——“回声”,*,和出生在(日期)美元,,由于壳牌如何解析字符串(合并相邻的字符串,引用删除),结果在以下,单身,文字字符串:*出生在(日期)美元。由于在产生的别名定义中没有引用*,因此它将扩展到当前目录中所有文件/目录名的列表(路径名扩展)。

#2


3  

You chould use different quotes for surrounding the whole text and for inner strings.

对于整个文本和内部字符串,应该使用不同的引号。

Try changing it to

试着改变它

alias logspace="find /apps/ /opt/ -type f -size +100M -exec ls -lh {} \; | awk '{print $5, $9 }'"

In other words, your outer quotes should be different than the inner ones, so they don't mix.

换句话说,你的外部引语应该与内部引语不同,这样它们就不会混合。

#3


2  

Community wiki update:

社区wiki更新:

  • The redeeming feature of this answer is recognizing that the OP's problem lies in unescaped use of the string delimiters (') inside a string.
  • 这个答案的可取之处是识别OP的问题在于字符串分隔符(')的不可转义使用。
  • However, this answer contains general string-handling truths, but does NOT apply to (Bourne-like, POSIX-compatible) shell programming specifically, and thus does not address the OP's problem directly - see the comments.
  • 但是,这个答案包含了一般的字符串处理真理,但是不适用于(类似bourne, posix兼容的)shell编程,因此不能直接解决OP的问题——请参阅注释。

Note: Code snippets are meant to be pseudo code, not shell language.

注意:代码片段是伪代码,而不是shell语言。

Basic strings: You canNOT use the same quote within the string as the entire string is delimited with:

基本字符串:您不能在字符串中使用相同的引号,因为整个字符串是用:

foo='hello, 'world!'';
    ^--start string
            ^-- end string
             ^^^^^^^^--- unknown "garbage" causing syntax error.

You have to escape the internal strings:

你必须摆脱内部的束缚:

foo='hello, \'world!\'';
            ^--escape

This is true of pretty much EVERY programming language on the planet. If they don't provide escaping mechanisms, such as \, then you have to use alternate means, e.g.

这几乎适用于地球上的每一种编程语言。如果它们不提供转义机制,例如\,那么您必须使用替代方法,例如。

quotechar=chr(39); // single quote is ascii #39
foo='hello ,' & quotechar & 'world!' & quotechar;