处理 bash 脚本中的特殊字符
Handling special characters in bash script
我不熟悉 bash 脚本。也许这是一个愚蠢的问题。但我找不到答案。我正在编写一个 bash 脚本,该脚本模仿命令 ls -sh
的行为,但实际上使用 du -sh
来获取文件和文件夹大小。并对输出进行排序。很像 du -sh* | sort -h
的颜色。
#!/usr/bin/bash
if [ "$#" = "0" ]
then
du -sh *|awk -f /path/to/color-ls.awk|sort -h
else
du -sh $@|awk -f /path/to/color-ls.awk|sort -h
fi
其中 ls-color.awk 是:
# color-ls.awk
size=;
name=;
for (i=3; i<=NF; i++)
{
tmp=(name " " $i);
name=tmp
}
# filename=([=12=] ~ /'/)? ("\"" name "\""):("'" name "'")
filename=("'" name "'")
printf " "
cmd=("ls -d " filename " --color")
system(cmd)
使用 ls --color
为 du -sh
的输出着色的 awk 脚本
我的脚本适用于大多数文件名,即使是包含空格的文件名。但它有一些涉及特殊字符的问题,我不知道如何解决。
1。当 运行 没有参数时:
它正在解释任何包含导致错误的单引号的文件名
sh: 1: Syntax error: Unterminated quoted string
2。当 运行 带有参数时:
和没有参数一样的问题。它将带空格的文件名解释为两个名称。
示例:当在名为 VirtualBox VMs
的文件夹上使用时,或者当在我的主目录中将 *
作为参数给出时,它的输出是:
du: cannot access 'VirtualBox': No such file or directory
du: cannot access 'VMs': No such file or directory
3。我想要的:
我希望脚本跳过特殊字符并将它们原样传递给 du
4。我尝试了什么:
我尝试在每个文件名前后添加双引号
parse(){
for arg in $@
do
printf "\"$arg\"\n"
done
}
但似乎没有用。 du 不接受附加到文件名的引号。
du: cannot access '"VirtualBox': No such file or directory
du: cannot access 'VMs"': No such file or directory
此外,用 \'
替换引号也无济于事。也许我只是做错了。
# du -sh $(printf "file'name\n" |sed "s/'/\\'/g")
du: cannot access 'file\'\''name': No such file or directory
# ls file\'name
"file'name"
空格也是如此
du: cannot access 'VirtualBox\': No such file or directory
du: cannot access 'VMs': No such file or directory
5。额外:
我正在尝试使脚本正常工作 ls -sh
会工作,但输出已排序并且在涉及文件夹时具有更准确的结果。但是当向它提供参数时,此脚本的工作方式类似于 ls -sh -d
。 lh Desktop
显示桌面的大小,而不是桌面内单个文件和文件夹的大小。我相信这可以通过一个循环来解决,该循环检查每个参数是文件还是文件夹并相应地执行 du -sh
然后排序。
#!/usr/bin/bash
if [ "$#" = "0" ]
then
du -sh *|awk -f /path/to/color-ls.awk|sort -h
else
for i in $@
do
if [[ -d "$i" ]]; then
du -sh $i/* |awk -f /path/to/color-ls.awk
else
du -sh "$i" |awk -f /path/to/color-ls.awk
fi
done|sort -h
fi
我希望找到最佳方法。
提前致谢。
由于您没有包含 shopt -s nullglob
,很可能 Desktop/*
没有扩展到任何奇怪的文件,除非那里真的没有文件,您启用了 nullglob
在交互模式下,du -sh
实际上不显示 Desktop
.
中文件的大小
您也可能从 Desktop/
不存在的地方调用脚本。
您可以添加打印 $PWD
的调试语句。您也可以使用 bash -x
.
尝试 运行 脚本
在你的脚本中,我建议启用 nullglob
然后修改它,这样如果目标目录不包含文件,就不会调用 du -sh
。
类似于:
set -- "$i"/*; [[ $# -gt 0 ]] && du -sh -- "$@" ...
另外$@
展开时要加引号
for i in "$@"; do
这可以简化为for i; do
,但我们将修改循环内的位置参数,因此我们展开"$@"
。
您也可以选择将展开的文件存储在数组中。
一个问题请不要post这么多。请每题一道题。每个问题一个脚本等
确保使用 shellcheck 检查您的脚本。它会抓住你的错误。参见 https://mywiki.wooledge.org/Quotes。
- When run without arguments:
awk 脚本中的 filename=("'" name "'")
是将带有 '
引号的任何内容传递给 system()
调用的无效方式,因此您会收到未终止的 '
错误,正如预期的那样,因为会有 3 '
个字符。修复 AWS 脚本,或者更好地在 Bash 中重写它,不需要 awk。也许用 Python 或 Perl 重写它。
此外,tmp=(name " " $i);
从文件名中删除制表符和多个空格。这一切都是为了只使用漂亮的文件名。
脚本无论如何都会在文件名中的换行符处中断。
- When run with arguments:
$@
进行了 分词 和 文件名扩展 (您应该研究的主题)。单词拆分将输入拆分为空格上的单词。使用 "$@"
。引用扩展。
- What I want:
您将使用 "$@"
- What I tried:
变量内容无关。您必须更改 使用 变量的方式,而不是它的内容。 IE。在变量的 use 周围使用引号。不是内容。
- Extra:
你没有引用扩展。使用 "$i"
而不是 $i
。是 "$i"/*
。 </code> 进行分词。</p>
<hr />
<p>最后,在所有这些之后,使用 GNU 工具,您的脚本可能看起来像:</p>
<pre><code>if (($# == 0)); then
set -- *
fi
du -hs0 "$@" |
sort -zh |
sed -z 's/\t/\x00/' |
while IFS= read -r -d '' size && IFS= read -r -d '' file; do
printf "%s " "$size";
ls -d "$file"
done
另见 How can I find and safely handle file names containing newlines, spaces or both? https://mywiki.wooledge.org/BashFAQ/001。
此外,您可以链接任何语句:
if stuff; then
stuff1
else
stuff2
fi |
sort -h |
awk -f yourscriptrt
也不要重复自己 - 使用 bash 数组:
args=()
if stuff; then
args=(*)
else
args=("$@")
fi
du -hs "${args[@]}" | stuff...
这样 sort
就可以减少工作量,我会把它放在 du
之后,而不是解析之后。
我不熟悉 bash 脚本。也许这是一个愚蠢的问题。但我找不到答案。我正在编写一个 bash 脚本,该脚本模仿命令 ls -sh
的行为,但实际上使用 du -sh
来获取文件和文件夹大小。并对输出进行排序。很像 du -sh* | sort -h
的颜色。
#!/usr/bin/bash
if [ "$#" = "0" ]
then
du -sh *|awk -f /path/to/color-ls.awk|sort -h
else
du -sh $@|awk -f /path/to/color-ls.awk|sort -h
fi
其中 ls-color.awk 是:
# color-ls.awk
size=;
name=;
for (i=3; i<=NF; i++)
{
tmp=(name " " $i);
name=tmp
}
# filename=([=12=] ~ /'/)? ("\"" name "\""):("'" name "'")
filename=("'" name "'")
printf " "
cmd=("ls -d " filename " --color")
system(cmd)
使用 ls --color
为 du -sh
我的脚本适用于大多数文件名,即使是包含空格的文件名。但它有一些涉及特殊字符的问题,我不知道如何解决。
1。当 运行 没有参数时:
它正在解释任何包含导致错误的单引号的文件名
sh: 1: Syntax error: Unterminated quoted string
2。当 运行 带有参数时:
和没有参数一样的问题。它将带空格的文件名解释为两个名称。
示例:当在名为 VirtualBox VMs
的文件夹上使用时,或者当在我的主目录中将 *
作为参数给出时,它的输出是:
du: cannot access 'VirtualBox': No such file or directory
du: cannot access 'VMs': No such file or directory
3。我想要的:
我希望脚本跳过特殊字符并将它们原样传递给 du
4。我尝试了什么:
我尝试在每个文件名前后添加双引号
parse(){
for arg in $@
do
printf "\"$arg\"\n"
done
}
但似乎没有用。 du 不接受附加到文件名的引号。
du: cannot access '"VirtualBox': No such file or directory
du: cannot access 'VMs"': No such file or directory
此外,用 \'
替换引号也无济于事。也许我只是做错了。
# du -sh $(printf "file'name\n" |sed "s/'/\\'/g")
du: cannot access 'file\'\''name': No such file or directory
# ls file\'name
"file'name"
空格也是如此
du: cannot access 'VirtualBox\': No such file or directory
du: cannot access 'VMs': No such file or directory
5。额外:
我正在尝试使脚本正常工作 ls -sh
会工作,但输出已排序并且在涉及文件夹时具有更准确的结果。但是当向它提供参数时,此脚本的工作方式类似于 ls -sh -d
。 lh Desktop
显示桌面的大小,而不是桌面内单个文件和文件夹的大小。我相信这可以通过一个循环来解决,该循环检查每个参数是文件还是文件夹并相应地执行 du -sh
然后排序。
#!/usr/bin/bash
if [ "$#" = "0" ]
then
du -sh *|awk -f /path/to/color-ls.awk|sort -h
else
for i in $@
do
if [[ -d "$i" ]]; then
du -sh $i/* |awk -f /path/to/color-ls.awk
else
du -sh "$i" |awk -f /path/to/color-ls.awk
fi
done|sort -h
fi
我希望找到最佳方法。
提前致谢。
由于您没有包含 shopt -s nullglob
,很可能 Desktop/*
没有扩展到任何奇怪的文件,除非那里真的没有文件,您启用了 nullglob
在交互模式下,du -sh
实际上不显示 Desktop
.
您也可能从 Desktop/
不存在的地方调用脚本。
您可以添加打印 $PWD
的调试语句。您也可以使用 bash -x
.
在你的脚本中,我建议启用 nullglob
然后修改它,这样如果目标目录不包含文件,就不会调用 du -sh
。
类似于:
set -- "$i"/*; [[ $# -gt 0 ]] && du -sh -- "$@" ...
另外$@
展开时要加引号
for i in "$@"; do
这可以简化为for i; do
,但我们将修改循环内的位置参数,因此我们展开"$@"
。
您也可以选择将展开的文件存储在数组中。
一个问题请不要post这么多。请每题一道题。每个问题一个脚本等
确保使用 shellcheck 检查您的脚本。它会抓住你的错误。参见 https://mywiki.wooledge.org/Quotes。
awk 脚本中的
- When run without arguments:
filename=("'" name "'")
是将带有 '
引号的任何内容传递给 system()
调用的无效方式,因此您会收到未终止的 '
错误,正如预期的那样,因为会有 3 '
个字符。修复 AWS 脚本,或者更好地在 Bash 中重写它,不需要 awk。也许用 Python 或 Perl 重写它。
此外,tmp=(name " " $i);
从文件名中删除制表符和多个空格。这一切都是为了只使用漂亮的文件名。
脚本无论如何都会在文件名中的换行符处中断。
- When run with arguments:
$@
进行了 分词 和 文件名扩展 (您应该研究的主题)。单词拆分将输入拆分为空格上的单词。使用 "$@"
。引用扩展。
- What I want:
您将使用 "$@"
- What I tried:
变量内容无关。您必须更改 使用 变量的方式,而不是它的内容。 IE。在变量的 use 周围使用引号。不是内容。
- Extra:
你没有引用扩展。使用 "$i"
而不是 $i
。是 "$i"/*
。 </code> 进行分词。</p>
<hr />
<p>最后,在所有这些之后,使用 GNU 工具,您的脚本可能看起来像:</p>
<pre><code>if (($# == 0)); then
set -- *
fi
du -hs0 "$@" |
sort -zh |
sed -z 's/\t/\x00/' |
while IFS= read -r -d '' size && IFS= read -r -d '' file; do
printf "%s " "$size";
ls -d "$file"
done
另见 How can I find and safely handle file names containing newlines, spaces or both? https://mywiki.wooledge.org/BashFAQ/001。
此外,您可以链接任何语句:
if stuff; then
stuff1
else
stuff2
fi |
sort -h |
awk -f yourscriptrt
也不要重复自己 - 使用 bash 数组:
args=()
if stuff; then
args=(*)
else
args=("$@")
fi
du -hs "${args[@]}" | stuff...
这样 sort
就可以减少工作量,我会把它放在 du
之后,而不是解析之后。