我正在尝试编写一个脚本,该脚本将用“-”替换空格,并使当前目录中的所有文件的所有字母小写。
for x in 'ls'
do
if [ ! -f $x ]; then
continue
fi
lc = `echo $x | tr '[A-Z]' '[a-z]'`
if [ $lc != $x ]; then
mv $x $lc
fi
done
find -name "* *" -type f | rename 's/ /-/g'
我得到以下输出: call: rename from to files...
但是名称没有改变,例如:252680610243-Analyzed Sample2 2Jul12.txt
我使用 更改了权限chmod 706
,这会导致问题吗?我在这里缺少什么?
这是输出bash -x lower.sh
:
+ for x in ''\''ls'\'''
+ '[' '!' -f ls ']'
+ continue
+ find -name '* *' -type f
+ rename 's/ /-/g'
call: rename from to files...
答案1
在 Debian 及其衍生版本(包括 Ubuntu)上,只需简单调用即可轻松实现rename
:
$ touch 'A B'
$ rename 'tr/ A-Z/-a-z/' -- *
$ ls
a-b
但你的rename
是一个具有相同名称的不同实用程序。
答案2
您的脚本存在几个问题。
for x in 'ls'
您正在迭代包含一个单词的列表:ls
。您可能打算编写`ls`
(带反引号)来解析ls
.不解析输出ls
:如果文件名包含特殊字符(例如空格),这将不起作用。 shell 已经有一种列出目录中文件的内置方法:通配符。所以写for x in *
。
if [ ! -f $x ]; then
如果文件名包含空格和其他特殊字符,这将不起作用,因为当您在$x
外部写入引号时,结果会被分割成单独的单词(并且每个单词都会被解释为通配符模式,以启动)。为了避免这种效果,请放入$x
双引号:if [ ! -f "$x" ]; then
。你可以记住复杂的规则, 要不就始终在变量替换周围使用双引号。
由于缺少双引号,其他几行被破坏。只需将它们放在每个变量替换周围即可。
lc = `echo $x | tr '[A-Z]' '[a-z]'`
由于等号周围有空格,这将不起作用:该行执行命令lc
,并将其=
作为第一个参数(和其他参数)传递。 shell 中的赋值=
符号周围不能有空格。
您不需要将 的参数括起来tr
。在这里,该命令恰好起作用,因为除了小写之外,您还说要替换[
by[
和]
by 。]
我建议使用美元括号$(…)
而不是反引号`…`
来进行命令替换。它们是等效的,只是反引号在引用其中的内容时具有复杂的规则,而$(…)
具有非常直观的语法。总而言之,该命令应该是:lc=$(echo "$x" | tr A-Z a-z)
find -name "* *" -type f | rename 's/ /-/g'
您的错误消息表明您有rename
来自 util-linux 包的命令而不是Perl脚本在 Debian 及其衍生版本(包括 Ubuntu)上找到。您使用的语法仅对 Perl 脚本有意义。
如果您要使用 Perl 脚本,它可以将大小写更改为小写,因此您无需事先执行此操作。并且您不需要调用,find
除非您也想重命名子目录中的文件(您的第一部分不处理)。如果你想重命名当前目录下的所有文件,你可以这样写:
prename 'tr/A-Z /a-z-/' -- *
如果您只想重命名常规文件(而不是子目录、符号链接等),请使用find
:
find . -type f -maxdepth 1 -exec prename 'tr/A-Z /a-z-/' {} +
如果您还想对子目录中的文件进行操作,并且目录名称可能包含空格或大写字母,则必须注意不要保留它们。由于您使用的是 Linux,因此您可以使用不包含目录名称的参数进行调用-execdir
。prename
find . -type f -execdir prename 'tr/A-Z /a-z-/' {} +
如果您没有 Perlrename
实用程序怎么办? util-linuxrename
实用程序在这里无法为您提供帮助。您可以在循环中再放置一个替代品。
for x in ./*; do
if [ -f "$x" ]; then
y=$(echo "$x" | tr 'A-Z-' 'a-z ')
mv "$x" "$y"
fi
done
使用 bash,您不需要调用tr
,您可以使用它的字符串替换结构。
for x in ./*; do
if [ -f "$x" ]; then
lc=${x,,} # convert all letters to lowercase
y=${lc// /-} # replace spaces by hyphens
if [ "$x" != "$y" ]; then
mv "$x" "$y"
fi
fi
done
如果您还想对子目录中的文件进行操作,则可以使用递归 glob(由选项启用globstar
)。再次强调,如果目录部分可能包含大写字母或空格,请注意不要打扰它们。
shopt -s globstar
for x in ./**/*; do
if [ -f "$x" ]; then
old_basename=${x##*/}
new_basename=${old_basename,,}
new_basename=${new_basename// /-}
if [ "$new_basename" != "$old_basename" ]; then
mv "${x%/*}/$old_basename" "${x%/*}/$new_basename"
fi
fi
done
在 zsh 中,输入autoload -U zmv
您的.zshrc
,您可以使用zmv
与全局限定符 .
仅匹配常规文件和参数扩展构造对于重命名:
zmv -Q '*(.)' '${(L)f// /-}'
也可以对子目录中的文件进行操作:
zmv -Qw '**/*(.)' '$1${(L)2// /-}'
答案3
主要问题是您没有迭代文件:您正在迭代单个字符串"ls"
.您可能打算使用反引号而不是单引号。尽管如此,不解析ls
。
您还需要引用您的变量,特别是因为您知道它们包含空格。
就在 bash 中,你可以这样做:
declare -l lc # whatever is stored in $lc is automatically lower cased
for x in *; do
lc=${x//[[:space:]]/} # replace all whitespace with nothing
[ "$lc" != "$x" ] && mv -- "$x" "$lc"
done
答案4
在 Bash 4 中 ${var,,} 将 var 转换为小写:
for f in *; do f2=${f,,}; mv "$f" "${f2// /-}"; done