如何在bash脚本中使用\ t替换或转义<tab>字符并能够输出单引号?</tab>

时间:2013-05-31 13:24:51

标签: bash shell escaping

在从一行(bash)命令创建文件的目标中,目标是输出任何文本文件的内容 - 在本例中为bash脚本 - 并将每行包装在能够输出的命令中粘贴在终端窗口中的同一行。

示例源输入文件:

Line 1
Line 2
Line 3

示例所需输出:

echo 'Line 1';echo 'Line 2';echo 'Line 3';

注意:只要源是人类可读的,用printfecho或其他命令来创建输出都无关紧要。

一个障碍是单引号,不会重新创建。因此,请使用特殊处理的 $'string'形式。单词扩展为字符串,替换为ANSI C标准指定的反斜杠转义字符。

另一个要求是从新文件中的旧文件重新创建制表符。因此希望替换&lt; \ tab&gt; \ t 的字符。

我们尝试使用sedtr执行此操作失败。如何使用escape \ t对应替换制表符,并且仍能输出带有原始引号的行?

输入文件/Library/Scripts/BootRepairMount.sh包含:

$ cat /Library/Scripts/BootRepairMount.sh
#!/bin/bash
sleep 18
for OUTPUT in $(diskutil list | grep ':                  Apple_HFS' | awk '{ print $NF }')
do
    if [[ -z $(df -lnh | grep /dev/$OUTPUT) ]]; then
        echo "$OUTPUT is not mounted, repair and mount"
        diskutil repairVolume $OUTPUT
        diskutil mount $OUTPUT
    fi
done

我们可以创建的最佳shell一行命令是:

$ oldifs=$IFS;printf '\n';printf '{';while IFS= read -r p;do [[ "$p" == *"'"* ]] && echo -n "echo $'$p';" || echo -n "echo '$p';"; done < /Library/Scripts/BootRepairMount.sh | tr '\t' '\134\164';printf '}';printf '\n\n';IFS=$oldifs

返回此错误输出:

{echo '#!/bin/bash';echo 'sleep 18';echo $'for OUTPUT in $(diskutil list | grep ': Apple_HFS' | awk '{ print $NF }')';echo 'do';echo '\if [[ -z $(df -lnh | grep /dev/$OUTPUT) ]]; then';echo '\\echo "$OUTPUT is not mounted, repair and mount"';echo '\\diskutil repairVolume $OUTPUT';echo '\\diskutil mount $OUTPUT';echo '\fi';echo 'done';}

所需的输出是:

{echo '#!/bin/bash';echo 'sleep 18';echo $'for OUTPUT in $(diskutil list | grep ': Apple_HFS' | awk '{ print $NF }')';echo 'do';echo '\tif [[ -z $(df -lnh | grep /dev/$OUTPUT) ]]; then';echo '\t\techo "$OUTPUT is not mounted, repair and mount"';echo '\t\tdiskutil repairVolume $OUTPUT';echo '\t\tdiskutil mount $OUTPUT';echo '\tfi';echo 'done';}

Bash one line命令版本2

$ oldifs=$IFS;printf '\n';printf '{';while IFS= read -r p;do [[ "$p" == *"'"* ]] && printf 'printf $'\''%q'\'';' "$p" || printf 'printf '\''%q'\'';' "$p"; done < /Library/Scripts/BootRepairMount.sh;printf '}';printf '\n\n';IFS=$oldifs

返回重度转义的输出:

{printf '\#\!/bin/bash';printf 'sleep\ 18';printf $'for\ OUTPUT\ in\ \$\(diskutil\ list\ \|\ grep\ \':\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ Apple_HFS\'\ \|\ awk\ \'\{\ print\ \$NF\ \}\'\)';printf 'do';printf '$'\tif [[ -z $(df -lnh | grep /dev/$OUTPUT) ]]; then'';printf '$'\t\techo "$OUTPUT is not mounted, repair and mount"'';printf '$'\t\tdiskutil repairVolume $OUTPUT'';printf '$'\t\tdiskutil mount $OUTPUT'';printf '$'\tfi'';printf 'done';}

永远不会在Mac OS X 10.7.5中恢复到原来的值。

printf '\#\!/bin/bash';

输出:

\#\!/bin/bash 

以及:

echo -e '\#\!/bin/bash'

输出未转义的值

\#\!/bin/bash
根据{{​​1}}页面,

-e不是Mac OS X 10.7.5 echo命令的有效命令开关。

3 个答案:

答案 0 :(得分:2)

bash的内置命令printf具有处理此问题的%q格式代码:

printf '\n{ '; while IFS= read -r p; do printf "echo %q; " "$p"; done < /Library/Scripts/BootRepairMount.sh; printf '}\n\n'

不幸的是,它并不总是选择易于阅读的引用/转义模式。具体来说,它更倾向于转义单个元字符(例如空格),而不是将它们括在引号中:

{ echo \#\!/bin/bash; echo sleep\ 18; echo for\ OUTPUT\ in\ \$(diskutil\ list\ \|\ grep\ \':\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ Apple_HFS\'\ \|\ awk\ \'{\ print\ \$NF\ }\'); echo do; echo $'\tif [[ -z $(df -lnh | grep /dev/$OUTPUT) ]]; then'; echo $'\t\techo "$OUTPUT is not mounted, repair and mount"'; echo $'\t\tdiskutil repairVolume $OUTPUT'; echo $'\t\tdiskutil mount $OUTPUT'; echo $'\tfi'; echo done; }

答案 1 :(得分:1)

如果我理解正确,您需要将一条长行粘贴到Terminal.app并希望获取原始脚本的“源代码”。因此,需要一个脚本来生成单行脚本。

也许有点不寻常的解决方案,但它简单易行。

这里是名为test.sh的测试脚本(而不是你的BootReapirMount.sh)

for i in {1..10}
do
    date
done

这是生成器脚本mkecho.sh

#!/bin/bash
[[ ! -f "$1" ]] && echo "Need filename" && exit 1
asc=$(gzip < "$1" | base64)
echo "base64 -D <<<'$asc'| gzip -d"

现在,运行:

bash mkecho.sh test.sh

你会得到下一个:

base64 -D <<<'H4sIAASwqFEAA0vLL1LIVMjMU6g21NMzNKjlSsnn4kxJLEkFMvJSuQBZFmY0HwAAAA=='| gzip -d

如果您将上述内容复制并粘贴到终端中,它将显示原始test.sh

VARIANT2

如果您想直接执行该脚本,则应将mkecho.sh修改为下一个mkeval.sh

#!/bin/bash
[[ ! -f "$1" ]] && echo "Need filename" && exit 1
asc=$(gzip < "$1" | base64)
echo -n 'eval "$(base64 -D <<<"'
echo -n $asc
echo -n '" | gzip -d)"'
echo

运行时

bash mkeval.sh test.sh

将获得

eval "$(base64 -D <<<"H4sIAASwqFEAA0vLL1LIVMjMU6g21NMzNKjlSsnn4kxJLEkFMvJSuQBZFmY0HwAAAA==" | gzip -d)"

最后当您将其复制并粘贴到终端时,运行test.sh并获得:

Fri May 31 16:25:08 CEST 2013
... 8 lined deleted...
Fri May 31 16:25:08 CEST 2013

警告:因为脚本未针对所有可能的条件进行测试,也未针对重定向等进行测试 - 我确实不建议使用eval版本。

答案 2 :(得分:0)

sed 's/\\t/\\/g'


$ echo 'ffsd \tif [[ -z $' | sed 's/\\t/\\/g'
ffsd \if [[ -z $