【问题标题】:shell script: check directory name and convert to lowercaseshell脚本:检查目录名并转换为小写
【发布时间】:2012-09-23 07:04:01
【问题描述】:

我希望我的 bash 脚本检查运行它的目录的名称。比如:

#!/bin/bash

path=eval 'pwd'

dirname=eval 'basename $path'

但它不起作用:我明白了

./foo.sh: line 5: basename $path: command not found

我该如何解决?此外,一旦我让 dirname 包含正确的 dirname,我想将其转换为小写,以对其进行测试。我可以使用 awk 在命令行上执行此操作:

echo $dirname | awk '{print tolower($0)}'

但是如何将返回值捕获到变量中?

【问题讨论】:

    标签: bash shell awk


    【解决方案1】:

    这是我对What platform independent way to find directory of shell executable in shell script? 的回答的摘录,除了小写部分之外,它本身就完全回答了您的问题,在我看来,小写部分已在此处的其他答案中适当地解决了很多次。

    我的回答的独特之处在于,当我尝试为另一个问题编写它时,我遇到了您的确切问题 - 如何将函数的结果存储在变量中? strong> 好吧,正如你所看到的,在一些帮助下,我找到了一个非常简单且非常强大的解决方案:

    我可以向函数传递一种 messenger 变量,并在必要时用eval 取消对结果函数参数的$1 名称的任何显式使用,并且在函数例程完成后,我使用 eval 和反斜杠引用技巧为我的 messenger 变量分配我想要的值,而无需知道它的名称。

    完全公开,虽然这是我的问题的解决方案,但它不是无论如何我的解决方案。以前去过那里几次,但他的一些描述虽然可能很精彩,但有点超出我的范围,所以我认为如果在上一段中包含我自己的版本,其他人可能会受益。当然,一旦我理解了它就很容易理解,尤其是对于这个,我不得不长时间地思考它是如何工作的。无论如何,您可以在Rich's sh tricks 找到更多信息,我还在我自己的答案摘录下方摘录了他页面的相关部分。

    ... 摘录: ...

    虽然还不是严格意义上的 POSIX,但 realpath 自 2012 年以来就是一个 GNU 核心应用程序。完全披露:在我在 info coreutils TOC 中注意到它并立即想到 [the linked] 问题之前从未听说过它,但使用如下所示的功能应该可靠,(很快POSIXLY?),并且,我希望,有效 为其调用者提供绝对来源的$0:

    % _abs_0() { 
    > o1="${1%%/*}"; ${o1:="${1}"}; ${o1:=`realpath "${1}"`}; eval "$1=\${o1}"; 
    > }  
    % _abs_0 ${abs0:="${0}"} ; printf %s\\n "${abs0}"
    /no/more/dots/in/your/path2.sh
    

    编辑: 值得强调的是,此解决方案使用POSIX parameter expansion 首先检查路径是否真的需要扩展和解析,然后再尝试这样做。这应该通过信使变量返回一个绝对来源的$0 (值得注意的例外是它将保留symlinks)作为有效我可以想象它可以做到无论路径是否已经是绝对的。 ...

    (小修改:在文档中找到realpath 之前,我至少已经缩减了我的 [the version below] 版本,而不是依赖于时间字段 [就像在第一个 ps 命令中所做的那样],但是,公平的警告,在测试了一些之后,我不太相信 ps 在其命令路径扩展能力方面是完全可靠的)

    另一方面,你可以这样做:

    ps ww -fp $$ | grep -Eo '/[^:]*'"${0#*/}"
    
    eval "abs0=${`ps ww -fp $$ | grep -Eo ' /'`#?}"
    

    ... 来自Rich's sh tricks: ...

    从 shell 函数返回字符串

    从上面的命令替换陷阱可以看出,stdout 不是 shell 函数将字符串返回给调用者的好途径,除非输出采用尾随换行符不重要的格式。当然,对于旨在处理任意字符串的函数,这种做法是不可接受的。那么,有什么办法呢?

    试试这个:

    func () {
    body here
    eval "$1=\${foo}"
    }
    

    当然${foo} 可以用任何形式的替换来替换。这里的关键技巧是 eval 行和转义的使用。当主命令解析器构造 eval 的参数时,“$1” 被扩展。但是“${foo}” 在这个阶段没有展开,因为“$” 已经被引用了。相反,它在 eval 评估它的参数时被扩展。如果不清楚为什么这很重要,请考虑以下情况:

    foo='hello ; rm -rf /'
    dest=bar
    eval "$dest=$foo"
    

    当然,以下版本是完全安全的:

    foo='hello ; rm -rf /'
    dest=bar
    eval "$dest=\$foo"
    

    请注意,在原始示例中,“$1” 用于允许调用者将目标变量名称作为参数传递给函数。如果您的函数需要使用 shift 命令,例如将剩余参数处理为 “$@”,那么将 “$1” 的值保存在函数开头的临时变量中可能会很有用。

    【讨论】:

      【解决方案2】:

      您的代码不起作用,因为您使用的是单引号而不是双引号。单引号可防止变量扩展,因此$path 不会扩展为您要使用的路径,而是按原样使用,就好像它是一个字符串一样。

      由于同样的原因,您的 awk 调用也无法正常工作。

      虽然你可以解决用双引号替换单引号的问题,像这样:

      #!/bin/bash
      
      path=eval "pwd"
      dirname=eval "basename $path"
      

      我建议改用重音(在这种情况下为). There's no reason to useeval`。另外,您还可以使用它来收集您感兴趣的返回值:

      #!/bin/bash
      
      path=`pwd`
      dirname=`basename $path`
      variable=`echo $dirname | awk "{print tolower($0)}"`
      

      【讨论】:

      • 您的第一个代码示例不起作用。 (详情见我的回答。)
      【解决方案3】:

      表格

      x=y cmd
      

      表示将环境变量x临时设置为值y,然后运行cmd,这就是这些行的解释方式:

      path=eval 'pwd'
      dirname=eval 'basename $path'
      

      也就是说,它们根本没有按照您的预期进行操作,而是将环境变量设置为文字值 eval,然后运行(或找不到)命令。正如其他人所说,将命令结果插入字符串的方法是将其放入$(...)(首选)或`...`(旧版)中。而且,作为一般规则,将它们括在双引号中更安全(因为将任何插值引用括在引号中更安全)。

      path="$(pwd)"
      dirname="$(basename "$path")"
      

      (从技术上讲,在这种情况下,外部引号并不是绝对必要的。但是,我想说这仍然是一个好习惯。)

      【讨论】:

        【解决方案4】:

        eval 执行传递给它的命令,但它只返回命令退出状态码,所以你不能真正在 set 运算符中使用它。将命令嵌入到集合运算符中的方法是使用右单引号或$()

        所以脚本将如下所示:

        #!/bin/bash
        
        curr_path=$(pwd)
        echo $curr_path
        curr_dir=$(basename $curr_path)
        echo $curr_dir
        echo $curr_dir | awk '{print tolower($0)}'
        

        【讨论】:

          【解决方案5】:

          你可以改写成

          dirname=eval "basename $path"
          

          使用单引号,您不会获得 shell 扩展,但您希望 $path 得到扩展。

          顺便说一句:我建议使用

          path=$(basename $path)
          

          如果你做类似的事情,它会更通用,可读性更好

          path=$(basename $(pwd))
          

          或获取小写结果

          path=$(basename $(pwd) | awk '{print tolower($0)}')
          

          或

          path=$(basename $(pwd) | tr 'A-Z' 'a-z' )
          

          【讨论】:

          • +1 最后一个建议是tr。我会用tr [:upper:] [:lower:] 改进它
          • dirname=eval "basename $path" 不起作用,但其他都完美。谢谢!
          【解决方案6】:
          B=$(echo "Some text that has CAPITAL letters " | awk '{print tolower($0)}')
          

          【讨论】:

            【解决方案7】:

            为什么不使用:

            #!/bin/bash
            
            path=`pwd`
            dirname=`basename $path | awk '{print tolower($0)}'`
            

            或者,如果您想将其作为一个班轮:

            dirname=`pwd | xargs basename | awk '{print tolower($0)}'`
            

            【讨论】:

            • 谢谢!效果很好。不幸的是,我的键盘没有正确的刻度 :( 幸运的是,我能够将您的代码复制并粘贴到我的脚本中。
            • @DeltaIV 很高兴听到!我想你总是可以输入 ALT-96 来获取它:-)
            • 使用$(pwd) 而不是反引号。它们比反引号更容易嵌套,并且更明显。另外,你的键盘上有这些键:)
            猜你喜欢
            • 1970-01-01
            • 2012-08-22
            • 2010-12-09
            • 2016-01-19
            • 2013-08-14
            • 2017-01-12
            • 2014-06-05
            • 1970-01-01
            • 2015-01-08
            相关资源
            最近更新 更多