【问题标题】:Bash script invalid space signBash 脚本无效的空格符号
【发布时间】:2016-07-26 23:14:40
【问题描述】:

我的目标是在目录中最后修改文件的秒数。

我的目录名称中有空格,想用脚本stat 它。使用终端,我只需使用:

sudo stat -c %Y /var/www/pre/data/jko/files/My\ Files

并且完美运行。 在我的bash 脚本中,我从目录/var/www/pre/data/jko/files/ 中一一读取文件(这是在while 循环中)

touch tempFile.txt
#ls sort by last modified date
sudo ls -rt /var/www/pre/data/$line/files/ > tempFile.txt 
# read newest file
outputFile=$(tail -1 tempFile.txt) 
# replace all spaces with "\ " sign
outputFile=$(echo ${outputFile// /"\ "})
outputDirectoryToFile=/var/www/pre/data/$line/files/$outputFile
echo $outputDirectoryToFile
expr `sudo date +%s` - `sudo stat -c %Y $outputDirectoryToFile`

如果我用bash script.sh 触发这个脚本,我得到了

/var/www/pre/data/jko/files/My\ Files //line from echo
stat: cannot stat '/var/www/pre/data/jko/files/My\': No such file or directory
stat: cannot stat 'Files': No such file or directory
expr: syntax error

或者也许有更简单的解决方案

【问题讨论】:

  • 引用它:sudo stat -c '%Y' "/var/www/pre/data/jko/files/My Files"
  • 创建文字引号(数据中的引号)决不能替代缺少的句法引号(代码中的引号)。它们是完全不同的两件事。
  • 顺便说一句,请参阅mywiki.wooledge.org/ParsingLs -- ls 不应以编程方式使用。
  • ...BashFAQ #3,mywiki.wooledge.org/BashFAQ/003,描述了有关查找最新/最旧/等的最佳实践。文件。
  • ...所以,整个${outputFile// /"\ "} 事情完全被误导了。删除它,并添加缺少的引号 - shellcheck.net 是一个出色的静态检查器(您可以下载并在本地安装),除其他外,它将查找脚本缺少正确性所需的引号的地方。

标签: linux bash shell


【解决方案1】:

正确高效地查找目录中的最新文件

{ read -r -d ' ' mtime && IFS= read -r -d '' filename; } \
  < <(find /directory -type f -printf '%T@ %p\0' | sort -z -r -n)

...会将最近修改的文件的时间(以秒为单位)放在 shell 变量 mtime 中,并将此文件的名称放在 shell 变量 filename 中。

此外,它甚至可以处理带有令人惊讶或故意恶意名称的文件——名称中带有换行符的文件名、带有 glob 字符的文件名等。我对这个成语以及它的工作原理有更完整的解释here .


为什么您的原始代码不起作用

现在,您的原始代码有什么问题?简而言之:文字引号不能代替句法引号。让我们来看看这意味着什么。

/var/www/pre/data/jko/files/My\ Files 中,反斜杠是语法:它是shell 语法。当您运行 stat /var/www/pre/data/jko/files/My\ Files 时,结果是一个如下所示的系统调用:

execv("/usr/bin/stat", "stat", "/var/www/pre/data/jko/files/My Files")

注意反斜杠是怎么消失的?那是因为 shell 在解析字符串时消耗了它。

以下都是一模一样的

# each of these assigns the same string to the variable named s
s=Hello\ World
s=Hello" "World
s='Hello World'

...它们可以扩展如下:

# this passes the *exact* contents of that variable as an argument to stat, and thus tries
# to stat a file named Hello World (no literal quotes in the name):
stat "$s"

在上面,引号又是 句法——它们告诉 shell 不要将变量扩展的结果拆分为多个单独的单词或将其评估为一个 glob——而不是 literal,在这种情况下,它们将作为其参数的一部分传递给stat


那么,当您运行s=${s// /'\ '} 时会发生什么?您将 literal 反斜杠放入数据中。

之后:

s="Hello World"
s=${s// /'\ '}    # change string to 'Hello\ World'
stat "$s"         # try to stat a file named 'Hello\ World', with the backslash 

如果在展开式中省略句法双引号会怎样?更丑:

s="Hello World"
s=${s// /'\ '}    # change string to 'Hello\ World'
stat $s           # try to stat a file named 'Hello\', and a second file named 'World'

那是因为未加引号的扩展并没有贯穿所有的 shell 解析步骤;它只进行字段拆分和全局扩展。

【讨论】:

    【解决方案2】:

    如果路径包含空格,请将它们全部引用以避免出现问题:

    touch tempFile.txt
    sudo ls -rt "/var/www/pre/data/$line/files/" > tempFile.txt //ls sort by last modified date
    outputFile=$(tail -1 tempFile.txt) // read newest file
    outputDirectoryToFile="/var/www/pre/data/$line/files/$outputFile"
    echo $outputDirectoryToFile
    expr `sudo date +%s` - `sudo stat -c %Y "$outputDirectoryToFile"`
    

    【讨论】:

    • 这仍然有一些未引用的扩展,尽管它们不太可能引起问题。 (例如,为生成expr 参数而调用的命令替换是不带引号的;echo 的参数也是如此——例如,IFS=/ 或文件名是否包含可以评估的内容可能很重要作为一个整体)。
    猜你喜欢
    • 1970-01-01
    • 2012-11-15
    • 2013-04-01
    • 2017-08-06
    • 1970-01-01
    • 2012-05-30
    • 1970-01-01
    • 2020-04-06
    • 1970-01-01
    相关资源
    最近更新 更多