脚本中字符串的独立于 shell 的转义

脚本中字符串的独立于 shell 的转义

我只是(再次)为此苦苦挣扎:

# only in bash
NORM=$'\033[0m'
NORM=$'\e[0m'

# only in dash
NORM='\033[0m'

# only in bash and busybox
NORM=$(echo -en '\033[0m')

目标是在字符串中包含特殊字符,不仅用于输出使用,echo还用于通过管道传输到 cli 工具等。

在上面的特定用例中,使用$(tput ...)可能是最好的方法,但我要求提供一个通用的转义解决方案,对外部工具的要求最低,但兼容性最大。

通常,我会使用“[ -z "$BASH_VERSION" ]但是”这样的条件来帮助自己

  1. 我还没有找到检测 busybox 的简单方法
  2. 5 行中的普通变量赋值(使用 if/else/fi)看起来有点矫枉过正
  3. 我更喜欢简单的解决方案

答案1

你想要的是"$(printf ...)"。斯蒂芬已经写了一篇出色的揭露printfecho,更多的是一篇文章,而不仅仅是一个答案,所以我不会在这里重复整个内容。与当前问题相关的主题演讲是:

  1. 依照POSIX 特性而且它非常便携,并且
  2. 它通常是 shell 内置函数,在这种情况下您没有外部调用或依赖项。

我还要补充一点,我花了很长时间(好吧,只有几周)才从 切换到echo,因为我已经习惯了echo,并且认为printf会很复杂。 (所有这些%标志都是关于什么的,嗯?)事实证明,实际上非常简单,除了末尾带有换行符的固定文本之外,printf我不再为任何事情而烦恼。echo


Printf 变得简单

有大量的选项printf。您可以打印精确到特定小数位的数字。您可以打印多个字段,每个字段都有指定的固定宽度(或最小宽度或最大宽度)。您可以使包含字符序列的 shell 字符串变量\t\n将这些字符序列解释为制表符和换行符来打印。

您可以做所有这些事情,并且您应该知道它们是可能的,以便您可以在需要时查找它们,但是在大多数情况下,您只需要了解以下内容:

printf将名为“format”的字符串作为其第一个参数。格式字符串可以指定如何处理进一步的参数(即如何格式化它们)。其他参数(如果在格式参数中根本没有引用*)是被忽略

由于字母数字字符(和其他字符)可以嵌入格式参数中并按原样打印,因此可能likeprintf正在做同样的事情,echo -n但由于某种未知的原因,它忽略了除第一个参数之外的所有参数。事实并非如此。

例如,尝试printf some test text.在这个例子中some实际上被视为格式,并且由于它不包含任何特殊内容,并且不告诉printf如何处理其余参数,因此它们将被忽略,您打印的只是some.

%需要在格式字符串( 的第一个参数)中使用后跟特定字母来printf指定后续参数包含的数据类型。 %s意思是“字符串”,并且是您最常使用的。

\n\t在格式中分别转换为换行符和制表符。

这确实是您高效使用所需的全部内容printf。请参阅以下代码块,了解一些非常简单的说明性示例。

$ var1="First"
$ var2="Second"
$ var3="Third"
$ printf "$var1" "$var2" "$var3"       # WRONG
First$                                 # Only the first arg is printed, without a trailing newline
$
$ printf '%s\n' "$var1"                # %s means that the next arg will be formatted as a literal string with any special characters printed exactly as-is.
First
$
$ printf '%s' "$var1" "$var2" "$var3"  # When more args are included than the format calls for, the format string is reused.  This example is equivalent to using '%s%s%s' as the format.
FirstSecondThird$                      # All three args were printed; no trailing newline.
$
$ printf '%s\t%s\t%s\n' "$var1" "$var2" "$var3"
First   Second  Third                  # Tab separation with trailing newline.  This format is very explicit about what to do with three arguments.  Now see what happens if four are used:
$ var4="Fourth"
$ printf '%s\t%s\t%s\n' "$var1" "$var2" "$var3" "$var4"
First   Second  Third             # The specified format is reused after the three expected args,
Fourth                            # so this line has two trailing tabs.
$
$ printf '%s\n' "$var1" "$var2" "$var3"  # This format reuse can be used to advantage in printing a list, for example.
First
Second
Third
$
$ printf '%s\t' "$var1" "$var2" "$var3" ; printf '\n'  # Here is a dual command that could have args added without changing the format string...
First   Second  Third   
$ printf '%s\t' "$var1" "$var2" "$var3" "$var4" ; printf '\n'
First   Second  Third   Fourth              # ...as you can see here.
$                                           # It does print a trailing tab before the newline, however.

* 当然,如果您包含单个参数格式说明符序列(例如 )%s,则整个格式字符串将根据需要多次重复使用,以处理提供的所有参数。请参阅示例。

答案2

printf适合打印任意八进制等,但你只是想输入ESC- 所以你只需输入即可。如果你担心你的终端会吃掉逃生,你可以这样做CTRL+V第一的。

所以...

NORM='^[[0m'

...输入上述序列后您可能会在屏幕上看到显示的内容,因为 tty 会以这种方式引用不可打印字符的输出,但 shell 会读取文字ESC中的字符^[转义序列的位置。

此功能独立于任何终端仿真器程序或 shell 或其他任何东西 - 尽管它是可配置的。系统内核按照stty实用程序的配置管理您的输入。默认设置 - 在 BSD、Linux 或(据我所知)几乎所有其他类 UNIX 系统 - 通常包括以下大部分内容:

stty --help 2>&1 | sed -e '/Special/,$!d;/./!q'

Special characters:
 * discard CHAR  CHAR will toggle discarding of output
   eof CHAR      CHAR will send an end of file (terminate the input)
   eol CHAR      CHAR will end the line
 * eol2 CHAR     alternate CHAR for ending the line
   erase CHAR    CHAR will erase the last character typed
   intr CHAR     CHAR will send an interrupt signal
   kill CHAR     CHAR will erase the current line
 * lnext CHAR    CHAR will enter the next character quoted
   quit CHAR     CHAR will send a quit signal
 * rprnt CHAR    CHAR will redraw the current line
   start CHAR    CHAR will restart the output after stopping it
   stop CHAR     CHAR will stop the output
   susp CHAR     CHAR will send a terminal stop signal
 * swtch CHAR    CHAR will switch to a different shell layer
 * werase CHAR   CHAR will erase the last word typed

下一个字符是通常配置的字符CTRL+V。它可以派上用场 - 你可以CTRL+V然后CTRL+J例如,在 shell 有机会读取换行符之前将换行符插入输入缓冲区。尝试一下 - 您可能会注意到您看不到 shell 的$PS2提示符,否则您会看到这样的提示符。对于劫持的 shellTAB键,您通常仍然可以通过在按键前面加上前缀来在命令行中逐字输入一个键CTRL+V。您可以输入文字CTRL+D EOF字符和文字CTRL+C INT人物(如果不使用zsh以同样的方式。

顺便说一句,这些字符中的大多数对于 shell 来说都不是特殊的,而且当按字面输入时甚至不需要引用(当然,尽管换行符会)

stty -a清楚地了解您的终端是如何配置的 - 它的配置很可能与我的和其他所有终端的配置大致相同。

如果你真的输入一个ESC在编辑 shell 脚本时,它会按原样保留在文件中 - 文字字符位于每次调用脚本时放置的位置,并且不需要扩展或其他解释来使其具有应有的含义。

相关内容