【问题标题】:bash: String Operator with regular expressionbash:带有正则表达式的字符串运算符
【发布时间】:2014-03-17 15:25:35
【问题描述】:

我想列出我的home 文件夹中的所有文件,并删除文件名中的#。 例如: #.emacs# 应打印为.emacs

这是我的代码

for dir in $(ls ~)
do
    # trim trailing
    filename="${dir#\#}"
    echo ${filename}
done

但它仍然显示以# 开头的文件,尽管我在终端中管理了正则表达式${dir#\#}

你能告诉我代码中的异味在哪里吗?

【问题讨论】:

  • 目前尚不清楚您要实现的目标是什么。你能举个例子吗?
  • 你能显示“之前和之后”的文件名吗?可能是您需要转义反斜杠 (filename="${dir#\\#}")?
  • 您似乎想mv 文件。而不是echo,说mv "${dir}" "${filename}"
  • 好的,更新后您应该会在BMW's 答案中看到第一个示例。您还应该将脚本更改为不解析 ls,除非您想引入其他错误。 (请注意,您可以使用 "${dir##*/}"basename "$dir" 删除前进的路径字符)
  • @BroSlow:我提到这一点是为了防止混淆不是偶然的:我认为您的意思是 preceding,而不是 proceeding -后者意味着别的东西。

标签: regex linux bash shell terminal


【解决方案1】:

从文件名中删除#,应该是:

filename="${dir//#/}"

edit:在某些系统(如Solaris)中,上面的命令不起作用,你需要转义。

filename="${dir//\#/}"

其余的都适用于 cygwin 和 Solaris。

如果您需要删除#之前的所有内容

filename="${dir##*#}"

如果您需要删除#之后的所有内容

filename="${dir%%#*}"

这是我从 bash Substring Replacement 复制和粘贴的完整说明

${string/substring/replacement}
Replace first match of $substring with $replacement.

${string//substring/replacement}
Replace all matches of $substring with $replacement.

${string%substring}
Deletes shortest match of $substring from back of $string.

${string%%substring}
Deletes longest match of $substring from back of $string.

${string#substring}
Deletes shortest match of $substring from front of $string.

${string##substring}
Deletes longest match of $substring from front of $string.

【讨论】:

  • 我在CYGWIN中测试过,你的环境是什么?
  • 添加更多解释
【解决方案2】:

Don't parse ls。您可以只使用 通配符扩展来代替。另外,您对参数扩展的使用是错误的,${word#something} 从前缀而不是后缀中删除了something。所以试试

#!/bin/bash
for dir in ~/*
do
  # trim trailing
  filename="${dir%#}"
  echo "${filename}"
done

【讨论】:

    【解决方案3】:

    这是一个 - 希望是指导性的版本:

    #!/usr/bin/env bash
    
    # Make pathname expansion match files that start with '.', too.
    shopt -s dotglob
    
    # Loop over all files/dirs. in the home folder.
    for f in ~/*; do
      # Exit, if no files/dirs match at all (this test may 
      # not be necessary if `shopt -s nullglob` is in effect).
      # Use -f to only match files, -d to only match dirs.
      [[ -e $f ]] || break
      # Remove the path component ... 
      filename=$(basename "$f")
      # ... and then all '#' chars. from the name.
      filename="${filename//#/}"
      # Process result
      echo "${filename}"
    done
    
    • 正如其他人所指出的,您不应解析 ls 输出 - 直接扩展 glob(通配符模式)的路径名始终是更好的选择。
    • shopt -s dotglob 确保名称以 . 开头的文件或目录包含在路径名扩展中。
    • 路径名扩展在路径组件完整的情况下发生,因此要从循环变量中获取纯文件名,必须(首先)应用basename,以便剥离路径组件。
    • 这里可能不是问题,但除非shopt -s nullglob 有效(默认情况下不是),否则不匹配任何内容的glob 不会被触及,因此使用无效文件名进入循环 - 因此[[ -e ... ]] 测试。

    【讨论】:

      【解决方案4】:

      您好,您只需echo 文件名,但不要重命名它。所以首先你需要从脚本中将cdhome 目录,然后重命名文件。请在下面的脚本中查找包含# 字符并从文件名中删除# 的文件名。

      #! /bin/bash
      cd ~
      for i in $(ls ~ )
      do 
       if [[ "${i}" == *#* ]]
       then
          var=$(echo "$i" | sed 's/#//' )
          printf "%s\n" "$var" #to print only
          #mv  "$i" "$var" #to renmae
       fi
      done
      

      【讨论】:

      • 我只是想在剥离后打印它的名字#
      • @TruongHa 好的,所以你只想打印它的名字而不是重命名它然后请看我编辑的帖子。
      • @TruongHa 你很高兴。
      【解决方案5】:

      您之前没有声明您的文件在文件名的开头结尾处有#。尝试类似:

      for dir in ~/*; do 
          filename="${dir#\#}"
          filename="${filename%\#}"
          echo "$dir ---> ${filename}"
      done
      

      或者使用BMW 所展示的作为他的第一个示例:

      for dir in ~/*; do 
          filename="${dir//#/}"
          echo "$dir ---> ${filename}"
      done
      

      一旦您对echo 的输出感到满意。您可以将其替换为 mv

      P.S:重申BroSlow 所说的内容。 Don’t parse ls.

      【讨论】:

        猜你喜欢
        • 2013-10-21
        • 2013-10-26
        • 2015-04-12
        • 1970-01-01
        • 2015-07-31
        • 2022-01-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多