Answers:
xargs
是工作的工具。那,或find
与-exec … {} +
。这些工具多次运行一个命令,并带有一次可以传递的尽可能多的参数。
当变量参数列表位于末尾时,这两种方法都更易于实现,在这里不是这种情况:to的最后一个参数mv
是目标。使用GNU实用程序(即在非嵌入式Linux或Cygwin上)时,-t
to 的选项mv
很有用,它首先传递目标。
如果文件名没有空格或\"'
,则只需提供文件名作为输入即可xargs
(该echo
命令是内置的bash,因此不受命令行长度限制):
echo !(*.jpg|*.png|*.bmp) | xargs mv -t targetdir
您可以使用该-0
选项来xargs
使用以空格分隔的输入,而不是默认的带引号的格式。
printf '%s\0' !(*.jpg|*.png|*.bmp) | xargs -0 mv -t targetdir
或者,您可以使用生成文件名列表find
。为避免递归到子目录,请使用-type d -prune
。由于未为列出的图像文件指定任何操作,因此仅其他文件被移动。
find . -name . -o -type d -prune -o \
-name '*.jpg' -o -name '*.png' -o -name '*.bmp' -o \
-exec mv -t targetdir/ {} +
(与外壳通配符方法不同,这包括点文件。)
如果没有GNU实用程序,则可以使用中间外壳程序以正确的顺序获取参数。此方法适用于所有POSIX系统。
find . -name . -o -type d -prune -o \
-name '*.jpg' -o -name '*.png' -o -name '*.bmp' -o \
-exec sh -c 'mv "$@" "$0"' targetdir/ {} +
在zsh中,您可以加载mv
内置函数:
setopt extended_glob
zmodload zsh/files
mv -- ^*.(jpg|png|bmp) targetdir/
或者,如果您愿意让mv
和其他名称继续引用外部命令:
setopt extended_glob
zmodload -Fm zsh/files b:zf_\*
zf_mv -- ^*.(jpg|png|bmp) targetdir/
或带有ksh样式的glob:
setopt ksh_glob
zmodload -Fm zsh/files b:zf_\*
zf_mv -- !(*.jpg|*.png|*.bmp) targetdir/
或者,使用GNU mv
和zargs
:
autoload -U zargs
setopt extended_glob
zargs -- ./^*.(jpg|png|bmp) -- mv -t targetdir/
shopt -s extglob
启用它。我错过了find
命令中的步骤,已经修复了它们。
find
我发布(现在)的命令起作用。复制粘贴时,您必须保留一部分。
!
?比起奇数尾部,它更明确,更容易理解-o
。例如,! -name '*.jpg' -a ! -name '*.png' -a ! -name '*.bmp'
如果使用Linux内核就足够了,那么您只需
ulimit -s 100000
之所以会奏效,是因为Linux内核大约在10年前包含了一个补丁,该补丁将参数限制更改为基于堆栈大小:https : //git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/提交/?id = b6a2fea39318e43fee84fa7b0b90d68bed92d2ba
更新:如果您感到勇敢,可以说
ulimit -s unlimited
只要有足够的RAM,您就可以进行任何Shell扩展。
ulimit -s unlimited
,它将对几乎不受限制的文件起作用。
ulimit -s unlimited
实际的命令行限制为2 ^ 31或2 GB。(MAX_ARG_STRLEN
在内核源代码中。)
操作系统的参数传递限制不适用于Shell解释器中发生的扩展。因此,除了使用xargs
or之外find
,我们还可以简单地使用shell循环将处理分解为单独的mv
命令:
for x in *; do case "$x" in *.jpg|*.png|*.bmp) ;; *) mv -- "$x" target ;; esac ; done
这仅使用POSIX Shell命令语言功能和实用程序。带有缩进的单行代码更加清晰,删除了不必要的分号:
for x in *; do
case "$x" in
*.jpg|*.png|*.bmp)
;; # nothing
*) # catch-all case
mv -- "$x" target
;;
esac
done
mv
进程,而不是仅使用POSIX find
解决方案@Gilles发布的几个进程。换句话说,这种方式导致大量不必要的CPU流失。
case
关于*
扩展结果以过滤出几个扩展名的语句等同于原始!(*.jpg|*.png|*.bmp)
表达式。该find
答案其实不等同; 它分为子目录(我看不到-maxdepth
谓词)。
-name . -o -type d -prune -o
防止下降到子目录。-maxdepth
显然不符合POSIX,尽管我的find
手册页中没有提到。
使用"$origin"/!(*.jpg|*.png|*.bmp)
代替catch块的更简单解决方案:
for file in "$origin"/!(*.jpg|*.png|*.bmp); do mv -- "$file" "$destination" ; done
感谢@Score_Under
对于多行脚本,您可以执行以下操作(请注意在删除;
之前done
):
for file in "$origin"/!(*.jpg|*.png|*.bmp); do # don't copy types *.jpg|*.png|*.bmp
mv -- "$file" "$destination"
done
要做一个更通用的移动所有文件的解决方案,您可以执行以下操作:
for file in "$origin"/*; do mv -- "$file" "$destination" ; done
如果执行缩进,则如下所示:
for file in "$origin"/*; do
mv -- "$file" "$destination"
done
这将把原始文件中的每个文件都一一地移到目的地。如果$file
文件名中包含空格或其他特殊字符,则必须使用引号引起来。
这是此方法完美运行的示例
for file in "/Users/william/Pictures/export_folder_111210/"*.jpg; do
mv -- "$file" "/Users/william/Desktop/southland/landingphotos/";
done
!(*.jpg|*.png|*.bmp)
。您可以通过遍历将其添加到for循环中,"$origin"/!(*.jpg|*.png|*.bmp)
从而避免了对Kaz答案中使用的开关的需求,并保持了for循环的简单主体。
这是我的两分钱,请附加到 .bash_profile
mv() {
if [[ -d $1 ]]; then #directory mv
/bin/mv $1 $2
elif [[ -f $1 ]]; then #file mv
/bin/mv $1 $2
else
for f in $1
do
source_path=$f
#echo $source_path
source_file=${source_path##*/}
#echo $source_file
destination_path=${2%/} #get rid of trailing forward slash
echo "Moving $f to $destination_path/$source_file"
/bin/mv $f $destination_path/$source_file
done
fi
}
export -f mv
用法
mv '*.jpg' ./destination/
mv '/path/*' ./destination/