展平目录,但在新文件名中保留目录名


11

如何以以下格式展平目录?

之前: ./aaa/bbb/ccc.png

后: ./aaa-bbb-ccc.png


附注:也请使用奇数字符(例如空格)说明文件名
Nathan JB

3
要如何处理可能的情况下,在那里您还需要指定./foo/bar/baz.png,并./foo-bar-baz.png同时存在。我假设您不想用前者代替后者?
2012年

在我的情况下,这是无关紧要的,但是答案的确应该说明这一点,以便其他人可以依靠它!谢谢!
弥敦道(Nathan JB)

Answers:


7

警告:我直接在浏览器中键入了大多数这些命令。注意事项。

使用zsh和zmv

zmv -o -i -Qn '(**/)(*)(D)' '${1//\//-}$2'

说明:该模式**/*以递归方式匹配当前目录子目录中的所有文件(它与当前目录中的文件不匹配,但是不需要重命名)。前两对括号是可以refered为团体$1$2置换文本中。最后一对括号添加了D glob限定符,因此不会省略点文件。-o -i表示将-i选项传递给,mv以便提示您是否将覆盖现有文件。


仅使用POSIX工具:

find . -depth -exec sh -c '
    for source; do
      case $source in ./*/*)
        target="$(printf %sz "${source#./}" | tr / -)";
        mv -i -- "$source" "${target%z}";;
      esac
    done
' _ {} +

说明:该case语句省略了当前目录和当前目录的顶级子目录。target包含源文件名($0),其中前导./句被去掉,所有斜杠均由破折号代替,最后还有zz如果文件名以换行符结尾,则存在最终名称:否则,命令替换将删除它。

如果您find不支持-exec … +(OpenBSD,我在找您):

find . -depth -exec sh -c '
    case $0 in ./*/*)
      target="$(printf %sz "${0#./}" | tr / -)";
      mv -i -- "$0" "${target%z}";;
    esac
' {} \;

使用bash(或ksh93),您无需调用外部命令即可将破折号替换为破折号,您可以将ksh93参数扩展与字符串替换构造一起使用${VAR//STRING/REPLACEMENT}

find . -depth -exec bash -c '
    for source; do
      case $source in ./*/*)
        source=${source#./}
        target="${source//\//-}";
        mv -i -- "$source" "$target";;
      esac
    done
' _ {} +

for source实例错过了第一次提出的文件/目录...我终于找到了为什么你(通常)已经使用_$0:) ...和伟大的答案感谢。我从他们那里学到了很多。
Peter.O 2012年

请注意,zmv示例只是进行了一次试运行:它打印出了移动命令,但没有执行它们。要实际执行这些命令,请删除-Q​​n中的n。
彼得

5

尽管这里已经有很好的答案,但我发现在以下方面更直观bash

find aaa -type f -exec sh -c 'new=$(echo "{}" | tr "/" "-" | tr " " "_"); mv "{}" "$new"' \;

aaa现有目录在哪里。它包含的文件将被移动到当前目录(aaa中仅留空目录)。这两个调用可同时tr处理目录和空格(对于转义斜线没有逻辑,这是读者的一项练习)。

我认为这更直观且相对容易调整。即,find如果说我只想查找.png文件,则可以更改参数。我可以更改tr通话,或根据需要添加更多通话。如果我想直观地检查它是否可以做对,我可以在echo前面添加mv(然后echo仅在看起来正确的情况下重复执行,而不做其他事情:

find aaa -name \*.png -exec sh -c 'new=$(echo "{}" | tr "/" "-" | tr " " "_"); echo mv "{}" "$new"' \;

另请注意,我使用的是find aaa...而不是find ....或find aaa/...,因为后两者会在最终文件名中留下有趣的瑕疵。


感谢您提供的这种衬板-当我从目录外部执行此操作时(对您而言,这正是您所说的),这对我来说非常有效。当我尝试从目录内部执行操作(希望避免添加根目录名称)时,所有文件“都消失了”。我已将它们转换为同一目录中的隐藏文件。
dgig

2
find . -mindepth 2 -type f -name '*' |
  perl -l000ne 'print $_;  s/\//-/g; s/^\.-/.\// and print' |
    xargs -0n2 mv 

注意:这不适用于包含的文件名\n
当然,这只会移动类型f文件...
唯一的名称冲突将来自pwd中预先存在的文件

用这个基本子集测试

rm -fr junk
rm -f  junk*hello*

mkdir -p  junk/junkier/junkiest
touch    'hello    hello'
touch    'junk/hello    hello'
touch    'junk/junkier/hello    hello'
touch    'junk/junkier/junkiest/hello    hello'

导致

./hello    hello
./junk-hello    hello
./junk-junkier-hello    hello
./junk-junkier-junkiest-hello    hello

0

这是一个ls版本..评论欢迎。它适用于像这样的怪异文件名 "j1ळ\n\001\n/j2/j3/j4/\nh  h\n",但是我真的很想知道任何陷阱。它更像是一种练习,“恶人ls实际上可以(健壮地)做到吗?”

ls -AbQR1p * |                        # paths in "quoted" \escaped format
    sed -n '/":$/,${/.[^/]$/p}' |     # skip files in pwd, blank and dir/ lines
    { bwd="$PWD"; while read -n1;     # save base dir strip leading "quote
                        read -r p; do # read path
        printf -vs "${p%\"*}"         # strip trailing quote"(:)
        [[ $p == *: ]] && {           # this is a directory
            cd   "$bwd/$s"            # change into new directory
            rnp="${s//\//-}"-         # relative name prefix
        } || mv "$s" "$bwd/$rnp$s"
      done; }

-1

只需将文件名+路径传递给tr命令即可。tr <replace> <with>

for FILE in `find aaa -name "*.png"`
do
  NEWFILE=`echo $FILE | tr '/' '-'`
  cp -v $FILE $NEWFILE
done

1
这不能安全地处理奇数文件名。空格会破坏它(除其他外)。
2012年

乍一看,这是一个干净,可读的解决方案,但是@ jw013是正确的,它不能很好地处理空间。换cp线会cp -v "$FILE" "$NEWFILE"有所帮助吗?(此外,您不应仅假设png文件。)
Nathan JB 2012年

2
@ NathanJ.Brauer在行中添加引号cp是不够的。findfor循环解析输出的整个方法都是有缺陷的。唯一安全的使用方式find是使用-exec,或者(-print0如果有的话)(便携性低于-exec)。我建议使用一个更可靠的替代方法,但您的问题目前未明确说明。
2012年

-2

安全的文件名中的空格:

#!/bin/bash

/bin/find $PWD -type f | while read FILE
do
    _new="${FILE//\//-}"
    _new="${_new:1}"
    #echo $FILE
    #echo $_new

    /bin/mv "$FILE" "$_new"
done

cp代替我的mv原因不是很明显,而是应该产生相同的结果。


3
type find-> find is /usr/bin/find在我的机器上。使用PATH作为脚本变量名是一个可怕的想法。另外,脚本会使用空格或反斜杠来修饰文件名,因为您滥用了该read命令(在此站点上搜索IFS read -r)。
吉尔斯(Gilles)'所以

find is hashed (/bin/find),相关如何?不同的发行版是不同的吗?
蒂姆(Tim)

知道我应该远离这种秃ul诱饵。
蒂姆(Tim)

@Tim您可以随时删除答案以恢复您的代表(我和我/她都需要花费代表才能降职)。硬编码到脚本中的路径似乎表明您缺少PATH环境变量的要点,这是每个 Unix系统使用的东西。如果您编写了/bin/find脚本,那么您的脚本实际上在没有的所有系统(Gilles,我的以及可能的OP)上都被破坏了/bin/find(例如b / c在其中/usr/bin/find)。在整点PATH的环境变量,使之成为非问题。
2012年

顺便说一下,$(pwd)已经在变量中可用了:$PWD。但是您不能在此处使用绝对路径:例如,如果您的脚本是从调用的/home/tim,它将尝试重命名/home/tim/some/path/home-tim-some-path
吉尔(Gilles)'所以
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.