AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / computer / 问题 / 1839879
Accepted
Frank van Wensveen
Frank van Wensveen
Asked: 2024-04-21 23:05:25 +0800 CST2024-04-21 23:05:25 +0800 CST 2024-04-21 23:05:25 +0800 CST

为什么 bash 脚本中的字符串变量在回显时的行为与传递给可执行文件时的行为不同?

  • 772

我有以下脚本:

#!/bin/bash

KID3=$(command -v kid3-cli)
ARG1="-c 'get'"
file="'1-01 - Johann Strauss - __Waldmeister___ Ouverture.flac'"

echo Command:
echo "$KID3" "$ARG1" "$file"

echo The kid3-cli output:
"$KID3" "$ARG1" "$file"

这会产生以下结果:

Command:
/usr/bin/kid3-cli -c 'get' '1-01 - Johann Strauss - __Waldmeister___ Ouverture.flac'
The kid3-cli output:
-c 'get', '1-01 - Johann Strauss - __Waldmeister___ Ouverture.flac' does not exist

请注意kid3-cli 输出中的逗号。我尝试过使用荣誉而不是引号(即${KID3} ${ARG1} ${file}),但这似乎没有任何区别。如果我使用多个命令变量($ARG1、$ARG2 等),则在 Kid3-cli 输出中每个变量后都会跟一个逗号。

当我将命令 ( /usr/bin/kid3-cli -c 'get' '1-01 - Johann Strauss - __Waldmeister___ Ouverture.flac') 复制并粘贴到交互式 bash shell 中(即在命令提示符下)时,它可以工作。

Kid3-cli 输出中的这些逗号从何而来,我缺少什么以及如何解决此问题?

command-line
  • 1 1 个回答
  • 54 Views

1 个回答

  • Voted
  1. Best Answer
    Kamil Maciorowski
    2024-04-22T05:40:05+08:002024-04-22T05:40:05+08:00

    工作命令

    当我将命令 ( /usr/bin/kid3-cli -c 'get' '1-01 - Johann Strauss - __Waldmeister___ Ouverture.flac') 复制并粘贴到交互式 bash shell 中(即在命令提示符下)时,它可以工作。

    一个好的起点是了解这里发生的事情;那么就更容易看出在其他情况下会发生其他事情。

    你的命令是:

    /usr/bin/kid3-cli -c 'get' '1-01 - Johann Strauss - __Waldmeister___ Ouverture.flac'
    

    shell 将命令拆分为单词,它使用未转义和未加引号的空格和制表符作为分隔符。单引号告诉 shell 这get是一个单词(即使没有引号,它也是一个单词),并且这1-01 - Johann Strauss - __Waldmeister___ Ouverture.flac是一个单词(没有引号,它也不会是一个单词)。实际上这些话是:

    • /usr/bin/kid3-cli
    • -c
    • get
    • 1-01 - Johann Strauss - __Waldmeister___ Ouverture.flac

    请注意,引号已完成其工作并已被删除。

    然后 shell 运行/usr/bin/kid3-cli并给出三个参数:-c、get和1-01 - Johann Strauss - __Waldmeister___ Ouverture.flac。kid3-cli无法知道是否以及在何处使用了引号。显然,树参数是工具可以毫无错误地理解的元组。


    麻烦的命令

    现在这个:

    "$KID3" "$ARG1" "$file"
    

    双引号非数组变量总是扩展为一个字(除非它是一个错误,例如由于set -u变量未设置)。这三个变量将扩展为恰好三个单词:

    • /usr/bin/kid3-cli从扩展的"$KID3"
    • -c 'get'从扩展的"$ARG1"
    • '1-01 - Johann Strauss - __Waldmeister___ Ouverture.flac'从扩展的"$file"

    扩展变量中出现的引号并不特殊。当(如果!)您告诉 shell(或另一个 shell,或其他什么)再次解析结果时(例如eval:),它们可能会很特别。这里你不必也不需要告诉 shell 再次解析结果;此外,这样做是一种不好的做法。

    shell 运行/usr/bin/kid3-cli并给出两个参数:-c 'get'和'1-01 - Johann Strauss - __Waldmeister___ Ouverture.flac'。不仅-c且get都在一个参数内;单引号也到达kid3-cli. 显然,这不是该工具可以按照您想要的方式理解的内容。


    关于什么echo?

    echo通过逐字打印其所有参数(解释为选项的参数除外)来工作,每个非最后一个参数与下一个参数之间有一个空格字符;默认情况下加上一个尾随换行符。

    echo "$KID3" "$ARG1" "$file"结果如下:

    • echo
    • /usr/bin/kid3-cli从扩展的"$KID3"
    • -c 'get'从扩展的"$ARG1"
    • '1-01 - Johann Strauss - __Waldmeister___ Ouverture.flac'从扩展的"$file"

    shell 运行echo并给出三个参数:/usr/bin/kid3-cli、-c 'get'和'1-01 - Johann Strauss - __Waldmeister___ Ouverture.flac'。你看:

    /usr/bin/kid3-cli -c 'get' '1-01 - Johann Strauss - __Waldmeister___ Ouverture.flac'
    

    其中一些空格来自变量,其他空格来自echo。echo请注意,如果get /usr/bin/kid3-cli、-c、'get'、'1-01、-、Johann Strauss、-和__Waldmeister___ Ouverture.flac'(8 个参数)或者echo将所有内容都作为一个长参数,则输出将是相同的。

    如果您echo在工作命令前面添加,即如果您给它的参数是工作命令的单词,即如果您运行:

    echo /usr/bin/kid3-cli -c 'get' '1-01 - Johann Strauss - __Waldmeister___ Ouverture.flac'
    

    那么输出将是:

    /usr/bin/kid3-cli -c get 1-01 - Johann Strauss - __Waldmeister___ Ouverture.flac
    

    再次没有任何指示添加了哪些空格echo。

    echo不是“可视化”论证的好工具。我个人比较喜欢printf '<%s>\n'。在你的情况下printf '<%s>\n' "$KID3" "$ARG1" "$file"会打印:

    </usr/bin/kid3-cli>
    <-c 'get'>
    <'1-01 - Johann Strauss - __Waldmeister___ Ouverture.flac'>
    

    很明显,-c 'get'这是一个单一的参数,单引号必须是printf。

    仍然存在歧义:如果一个参数是/usr/bin/kid3-cli>\n<-c 'get'(其中\n此处表示换行符,但我的意思是参数内的文字printf换行符),那么我会像单独的参数一样显示它:/usr/bin/kid3-cli和-c 'get'。尽管如此,内部争论>\n<还是很少见(至少在我的工作流程中;<%s>\n根据您的需要进行调整)。


    不加引号$ARG1

    如果您设置:

    ARG1="-c get"
    file="1-01 - Johann Strauss - __Waldmeister___ Ouverture.flac"
    

    如果你运行:

    "$KID3" $ARG1 "$file"
    

    那么这将起作用(使用默认值$IFS)。不加引号的$ARG1将被分成两个单词:-c和get。请注意,如果您想kid3-cli查看get(而不是'get'),那么您不能存储-c 'get'在里面ARG1。同样,file="'…'"这里也是错误的。

    $ARG1如果变量的值是静态的,完全在您的控制之下,没有触发文件名生成(通配符)的字符,那么不加引号并不是那么糟糕,这$IFS会导致将值精确地分割到您想要的位置。一般来说,你应该总是引用。

    最主要的是 shell 变量用于存储数据,而不是存储 shell 代码。在/usr/bin/kid3-cli -c 'get' …您键入的内容中,单引号对于 shell 在语法上是有意义的,后面的空格也是如此-c。这些部分是 shell 代码,将它们存储在变量中是错误的。有时(就像这里)您可以存储有意义的空间而不引用变量,但这仍然是一个不好的做法。

    这里的其他想法:我们如何运行存储在变量中的命令?


    解决方案

    (不是唯一的解决方案,请参阅已经链接的问题。一个可靠、安全、足够优雅的解决方案,适合您的代码结构。)

    在 Bash 中,您可以使用数组来可靠地存储多个单词:

    KID3=$(command -v kid3-cli)
    ARG1=( -c 'get' )
    file="1-01 - Johann Strauss - __Waldmeister___ Ouverture.flac"
    "$KID3" "${ARG1[@]}" "$file"
    

    单引号包围get是不需要的,我只是想告诉你它们可能在那里。"${ARG1[@]}"将扩展为两个词:-c和get。

    通过这种方法,您也可以只使用一个逐渐构建的数组:

    cmnd=( "$(command -v kid3-cli)" )
    cmnd+=( -c get )
    cmnd+=( '1-01 - Johann Strauss - __Waldmeister___ Ouverture.flac' )
    "${cmnd[@]}"
    

    (引号包含1-01 … ….flac很重要)。或者使用您一步构建的一个数组:

    cmnd=( "$(command -v kid3-cli)" -c get '1-01 - Johann Strauss - __Waldmeister___ Ouverture.flac' )
    "${cmnd[@]}"
    

    在每种情况下,生成的命令将等效于:

    command kid3-cli -c get "1-01 - Johann Strauss - __Waldmeister___ Ouverture.flac"
    

    除非您有充分的理由使用变量,否则您应该只运行上面的代码,甚至可能没有单词command。

    • 3

相关问题

  • 如何使用键盘快捷键在 macOS 终端中选择一行?

  • 如何在 macOS 的终端中切换切换(连续性)?

  • Windows 上是否有可以通过 CMD 或 Powershell 提供加密安全随机字节的设备或可执行文件?

  • Python 的“pass”参数的批处理等价物是什么?

  • 禁用后无法启用 Microsoft Print to PDF

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    如何减少“vmmem”进程的消耗?

    • 11 个回答
  • Marko Smith

    从 Microsoft Stream 下载视频

    • 4 个回答
  • Marko Smith

    Google Chrome DevTools 无法解析 SourceMap:chrome-extension

    • 6 个回答
  • Marko Smith

    Windows 照片查看器因为内存不足而无法运行?

    • 5 个回答
  • Marko Smith

    支持结束后如何激活 WindowsXP?

    • 6 个回答
  • Marko Smith

    远程桌面间歇性冻结

    • 7 个回答
  • Marko Smith

    子网掩码 /32 是什么意思?

    • 6 个回答
  • Marko Smith

    鼠标指针在 Windows 中按下的箭头键上移动?

    • 1 个回答
  • Marko Smith

    VirtualBox 无法以 VERR_NEM_VM_CREATE_FAILED 启动

    • 8 个回答
  • Marko Smith

    应用程序不会出现在 MacBook 的摄像头和麦克风隐私设置中

    • 5 个回答
  • Martin Hope
    Vickel Firefox 不再允许粘贴到 WhatsApp 网页中? 2023-08-18 05:04:35 +0800 CST
  • Martin Hope
    Saaru Lindestøkke 为什么使用 Python 的 tar 库时 tar.xz 文件比 macOS tar 小 15 倍? 2021-03-14 09:37:48 +0800 CST
  • Martin Hope
    CiaranWelsh 如何减少“vmmem”进程的消耗? 2020-06-10 02:06:58 +0800 CST
  • Martin Hope
    Jim Windows 10 搜索未加载,显示空白窗口 2020-02-06 03:28:26 +0800 CST
  • Martin Hope
    andre_ss6 远程桌面间歇性冻结 2019-09-11 12:56:40 +0800 CST
  • Martin Hope
    Riley Carney 为什么在 URL 后面加一个点会删除登录信息? 2019-08-06 10:59:24 +0800 CST
  • Martin Hope
    zdimension 鼠标指针在 Windows 中按下的箭头键上移动? 2019-08-04 06:39:57 +0800 CST
  • Martin Hope
    jonsca 我所有的 Firefox 附加组件突然被禁用了,我该如何重新启用它们? 2019-05-04 17:58:52 +0800 CST
  • Martin Hope
    MCK 是否可以使用文本创建二维码? 2019-04-02 06:32:14 +0800 CST
  • Martin Hope
    SoniEx2 更改 git init 默认分支名称 2019-04-01 06:16:56 +0800 CST

热门标签

windows-10 linux windows microsoft-excel networking ubuntu worksheet-function bash command-line hard-drive

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve