简体   繁体   English

处理 bash 脚本中的特殊字符

[英]Handling special characters in bash script

I'm not familiar with bash scripting.我不熟悉 bash 脚本。 Maybe this is a silly question.也许这是一个愚蠢的问题。 But I couldn't find the answer.但我找不到答案。 I'm working on a bash script that mimics the behavior of the command ls -sh but that actually uses du -sh to get file and folder sizes.我正在研究一个 bash 脚本,它模仿命令ls -sh的行为,但实际上使用du -sh来获取文件和文件夹大小。 And it sorts the output.它对 output 进行排序。 pretty much like du -sh* | sort -h很像du -sh* | sort -h du -sh* | sort -h with colors.使用 colors 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

where ls-color.awk is:其中ls-color.awk是:

# color-ls.awk
size=$1;
name=$2;
for (i=3; i<=NF; i++)
{
    tmp=(name " " $i);
    name=tmp
}
# filename=($0 ~ /'/)? ("\"" name "\""):("'" name "'")
filename=("'" name "'")
printf $1 " "
cmd=("ls -d " filename " --color")
system(cmd)

an awk script that uses ls --color to color the output of du -sh使用ls --color为 du -sh 的 output 着色的 awk 脚本

My scripts works fine with most file names even ones containing spaces.我的脚本适用于大多数文件名,甚至包含空格的文件名。 but it has some problems involving special characters that I didn't know how to fix.但它有一些涉及特殊字符的问题,我不知道如何解决。

1. When run without arguments: 1. 在没有 arguments 的情况下运行:

It is interpreting any file name that contains single quotes causing an error它正在解释任何包含导致错误的单引号的文件名

sh: 1: Syntax error: Unterminated quoted string

2. When run with arguments: 2. 使用 arguments 运行时:

The same problem as without arguments.与没有 arguments 的问题相同。 And it's interpreting a file name with spaces as two names.并且它将带有空格的文件名解释为两个名称。

example: when used on a folder named VirtualBox VMs or when given * as an argument in my home directory here's it's output:示例:当在名为VirtualBox VMs的文件夹上使用时,或者在我的主目录中将*作为参数时,它是 output:

du: cannot access 'VirtualBox': No such file or directory
du: cannot access 'VMs': No such file or directory

3. What I want: 3. 我想要的:

I want the script to skip special characters and pass them as they are to du我希望脚本跳过特殊字符并将它们按原样传递给du

4. What I tried: 4.我尝试了什么:

I tried adding double quotes before and after each file name我尝试在每个文件名之前和之后添加双引号

parse(){
    for arg in $@
    do
        printf "\"$arg\"\n"
    done
}

but it didn't seem to work.但它似乎没有用。 du doesn't accept quotes appended to the file name. du 不接受附加到文件名的引号。

du: cannot access '"VirtualBox': No such file or directory
du: cannot access 'VMs"': No such file or directory

Also, replacing quotes with \' doesn't help ether.此外,用\'替换引号对以太没有帮助。 maybe I'm just doing it wrong.也许我只是做错了。

# du -sh $(printf "file'name\n" |sed "s/'/\\\'/g")
du: cannot access 'file\'\''name': No such file or directory
# ls file\'name 
"file'name"

Same goes for spaces空间也是如此

du: cannot access 'VirtualBox\': No such file or directory
du: cannot access 'VMs': No such file or directory

5. Extra: 5. 额外:

I wanted to make the script works as normal ls -sh would work but with sorted output and with more accurate results when it comes to folders.我想让脚本像正常的ls -sh一样工作,但使用排序的 output 并且在文件夹方面具有更准确的结果。 but this script works like ls -sh -d when arguments are supplied to it.但是当向它提供 arguments 时,此脚本的工作方式与ls -sh -d类似。 making lh Desktop shows the size of Desktop instead of the size of the individual files and folders inside Desktop.使lh Desktop显示 Desktop 的大小,而不是 Desktop 中单个文件和文件夹的大小。 I believe this can be fixed with a loop that checks if each argument is a file or a folder and execute du -sh accordingly then sort.我相信这可以通过一个循环来解决,该循环检查每个参数是文件还是文件夹并相应地执行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

But for some reason it didn't work但由于某种原因它不起作用

# lh Desktop/
du: cannot access 'Desktop/*': No such file or directory

whereas using du -sh Desktop/* usually works in interactive shell.而使用du -sh Desktop/*通常适用于交互式 shell。

Thanks in advance.提前致谢。

Since you didn't include shopt -s nullglob , it's likely that Desktop/* didn't expand to any file which is odd unless there really are no files there, you have enabled nullglob in interactive mode, and du -sh doesn't actually display the sizes of the files in Desktop .由于您没有包含shopt -s nullglob ,因此Desktop/*很可能没有扩展到任何奇怪的文件,除非那里确实没有文件,您在交互模式下启用了nullglob ,而du -sh没有实际显示Desktop中文件的大小。

It's also likely that you're calling the script from where Desktop/ doesn't exist.您也可能在Desktop/不存在的地方调用脚本。

You can add a debug statement which prints $PWD .您可以添加一个打印$PWD的调试语句。 You can also try running the script with bash -x .您也可以尝试使用bash -x运行脚本。

In your script I suggest enabling nullglob and then modifying it so du -sh isn't called if target directory contains no files.在您的脚本中,我建议启用nullglob然后对其进行修改,以便如果目标目录不包含任何文件,则不会调用du -sh

Something like:就像是:

set -- "$i"/*; [[ $# -gt 0 ]] && du -sh -- "$@" ...

Also $@ should be quoted when being expanded.扩展时还应引用$@

for i in "$@"; do

This can be simplified to for i; do这可以简化为for i; do for i; do , but we will modify the positional parameters inside the loop so we expand "$@" instead. for i; do ,但我们将修改循环内的位置参数,因此我们展开"$@"

You can also choose to store the expanded files inside an array as well.您也可以选择将展开的文件存储在数组中。

Please do not post so much in one question.请不要在一个问题上发表这么多。 Please one problem per question.请每个问题一个问题。 One script per question, etc.每个问题一个脚本,等等。

Make sure to check your scripts with shellcheck .确保使用 shellcheck 检查您的脚本 It will catch your mistakes.它会抓住你的错误。 See https://mywiki.wooledge.org/Quotes .请参阅https://mywiki.wooledge.org/Quotes

  1. When run without arguments:在没有 arguments 的情况下运行时:

filename=("'" name "'") inside awk script is a invalid way to pass anything with ' quotes to system() call, so you are getting unterminated ' error, as expected. awk 脚本中的filename=("'" name "'")是一种将带有'引号的任何内容传递给system()调用的无效方法,因此正如预期的那样,您会遇到 unterminated '错误。 Fix the script, or better rewrite it in Bash, no need for awk, maybe in Python or Perl.修复脚本,或者在 Bash 中更好地重写它,不需要 awk,可能在 Python 或 Z014344AD06FEDA73E。

  1. When run with arguments:使用 arguments 运行时:

$@ undergoes word splitting and filename expansion (topics you should research), and word splitting the input into words on spaces. $@经历了分词文件名扩展(你应该研究的主题),并将输入的词分词成空格上的词。 Use "$@" .使用"$@" Qoute the expansions.引用扩展。

  1. What I want:我想要的是:

You'll be doing that with "$@"您将使用"$@"来执行此操作

  1. What I tried:我尝试了什么:

The variable content is irrelevant.可变内容无关紧要。 You have to change the way you use the variable, not it's content.你必须改变你使用变量的方式,而不是它的内容。 Ie use quotes.即使用引号。

  1. Extra:额外的:

You did not quote the expansion.你没有引用扩展。 Use "$i" not $i .使用"$i"而不是$i It's "$i"/* .它是"$i"/*

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM