2

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

示例源输入文件:

Line 1
Line 2
Line 3

示例所需的输出:

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

注意:只要源是人类可读的,是否使用 或其他命令来创建输出并不重要printfecho

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

另一个要求是在新文件中从旧文件重新创建制表符。因此希望用 \t 替换 <\tab> 字符

我们尝试这样做sedtr失败。如何用转义符 \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 一行命令版本 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

-eecho根据其man页面,它不是 Mac OS X 10.7.5 命令的有效命令开关。

4

3 回答 3

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; }

于 2013-05-31T15:11:34.790 回答
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

变体2

如果你想直接执行脚本,你应该修改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版本。

于 2013-05-31T14:36:03.160 回答
0
sed 's/\\t/\\/g'


$ echo 'ffsd \tif [[ -z $' | sed 's/\\t/\\/g'
ffsd \if [[ -z $
于 2013-05-31T13:33:50.260 回答