【问题标题】:What is the best way to do this string transformation in shell?在 shell 中进行这种字符串转换的最佳方法是什么?
【发布时间】:2019-06-02 09:01:03
【问题描述】:

我正在尝试编写一个需要转换以下形式的输入的 shell 脚本:

foo/bar/baz/qux.txt
bar/baz/quz.txt
baz/quz/foo.txt

进入:

baz-qux
quz
foo

即在 '/' 上拆分,删除前 2 段,删除 '.txt' 并用剩余的斜杠替换连字符。

使用 tr 替换似乎很简单:

paths=$(cat <<- EOF
foo/bar/baz/qux.txt
bar/baz/quz.txt
baz/quz/foo.txt
EOF
)

echo $paths | tr '/' '-' | tr '.txt' ' '

我尝试过各种形式的

cut -d '/' -f x

为了获得必要的细分,但我不够用。

我是一个红宝石人,所以很想拿起我的锤子,只使用红宝石:

lines.each { |s| s.split('/')[2..-1].join('-').split('.')[0] }

但是为这一操作部署 ruby​​ 似乎有点矫枉过正。而且我还是想提高我的 shell 技能,所以想知道是否有更优雅的方式有人会推荐在 shell 中执行此操作?

感谢您的帮助

【问题讨论】:

  • 命令tr '.txt' ' ' not 做你想做的事——它不会删除“.txt”,它将它转换为 4 个空格,它也可以与任何其他“t”、“x”和“.”相同。在名字里。例如,“test1.txt”->“es 1”。

标签: string bash shell awk sh


【解决方案1】:

可以使用bash parameter expansions:

for name in foo/bar/baz/qux.txt bar/baz/quz.txt baz/quz/foo.txt; do
    new=${name#*/}   # drop the shortest prefix match for */, thus everything up to first /
    new=${new#*/}    # repeat, dropping the second segment
    new=${new%.txt}  # drop shortest suffix match for .txt
    new=${new//\//-} # convert any remaining slashes
    echo "$new"
done

给予:

baz-qux
quz
foo

这些都是bash shell 内置结构,因此不需要像cutsedtr 这样的外部进程。

【讨论】:

  • 您是否接受将 cmets 移动到内联形式的编辑,以防止代码看起来比实际更长?
  • 当然是@CharlesDuffy。我曾考虑将肉放入一个函数中,如果代码适合我,我可能会这样做。
  • 鉴于样本输入的第一行,我认为new=${name##*/};new=${new%.txt} 就足够了吗?祝大家好运。
  • 感觉很密集 - 看不到最后剩下的 / 被更改为 - 的位置。
  • 是否有理由不使用new=${name#*/*/} 而不是两个扩展?
【解决方案2】:

你可以试试这样的:cut -d/ -f3- | cut -d. -f1 | tr / -

解释:

  • cut -d/ -f3- - 在“/”上拆分,并保留第三个字段及其后面的所有内容 (baz/qux.txt)
  • cut -d. -f1 - 在 '.' 上拆分,保留第一个值(删除文件扩展名)(baz/qux)
  • tr / - - 将任何剩余的“/”转换为“-”。 (baz-qux)

【讨论】:

  • 概念上很简单,但启动三个独立的进程是相当大的开销。
【解决方案3】:

您可以在一个sed 命令中完成所有操作

sed -E 's|([^/]*/){,2}||; s|/|-|g; s|\.txt$||' file

\.txt$ 替换为\.[^.]$ 以删除所有扩展名,而不仅仅是.txt

【讨论】:

  • 如果在一次运行中处理大量输入,这将是更好的方法(足以克服 sed 运行后更快的 I/O 的恒定时间启动成本),如果数字较小,则为 cdarke。
【解决方案4】:

试试 Perl

$ cat mark_smith.txt
foo/bar/baz/qux.txt
bar/baz/quz.txt
baz/quz/foo.txt

$ perl -F"/" -lane ' @a=@F[2..$#F]; @b=map{s/.txt//g;$_} @a; print join("-",@b) ' mark_smith.txt
baz-qux
quz
foo

$

【讨论】:

    【解决方案5】:

    假设 . 仅在文件名中

    $ awk -F[/.] '{n=NF; p=$(n-1)} n>4{p=$(n-2)"-"p} {print p}' file
    
    baz-qux
    quz
    foo
    

    【讨论】:

      【解决方案6】:
      awk '{gsub(/^.{8}|.txt$/,"")sub(/\//,"-")}1' file
      
      baz-qux
      quz
      foo
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-08-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多