我正在尝试用 zsh (Linux Mint 21.3 “Virginia”) 编写一个脚本,将文件从 $source 移动(或复制)到 $target,但结果却令人困惑。我使用 yad 文件对话框来选择文件并命名目标目录。我能做到的最好的就是只将最终选择复制到空目标。
以下是整个脚本
setopt -x
setopt -v
autoload -Uz zmv
# WARNING Just because two or more files from different sources have identical names does not mean
#that the files are identical.
declare -a array
array=$(yad --width=600 --height=800 \
--title="Choose files to move." \
--file --multiple --separator='\n')
echo $array
target=$(yad --width=600 --height=800 \
--title="Choose destination." \
--file --directory)
echo "target="$target
# The file you wish to move may already exist in the target directory.
# If you do not want to overwrite it you can use
# mv -n
# mv --no-clobber
# or
# mv --backup=numbered
# Clumsy, but better than nothing, and doesn't give me what I want.
# That is why I wrote this script, it adds an incremented number to the name.
# eg <movie>.<ext>
# eg <movie>_copy1.<ext>
# eg <movie>_copy2.<ext>
# Yes, I could use brackets but they are illegal characters and I have a renamer
# that would remove them.
# You can't just use <filename>.$ext to check if the file exists in target because
# you'll miss any existing copies. You have to account for iteration.
for filename in $array; do
unset x
# decompose filename
# just name without path
filename=$filename:t
echo
echo
echo "filename="$filename
echo
echo
# name without extension
shortname=${filename%.*}
echo "shortname="$shortname
echo
echo
# extension
ext=${filename##*.}
echo
echo
echo "ext="$ext
# The key here is to rename the file (if necessary) BEFORE copying it to target instead
# of after.
# Does file already exist in $target? Count them.
x=$(find $target -iname "$shortname*.$ext" | wc -c)
echo
echo
echo "x="$x
# If no file exists, move to target.
# If one file exists then $x will return "1". Think of it as
# <filename>_copy0.<ext>,
# this makes the file you wish to move
# <filename>_copy1.<ext>, - the value of $x.
case $x in
0) cp $filename $target
sleep .2
;;
*) mv $filename $shortname_copy$x.$ext
cp $filename $target
sleep .2
;;
esac
done
exit 0
这是使用相同参数两次运行脚本的终端输出。
+/home/revision-6/bin/cpi:4> setopt -v
autoload -Uz zmv
+/home/revision-6/bin/cpi:5> autoload -Uz zmv
# WARNING Just because two or more files from different sources have identical names does not mean
#that the files are identical.
declare -a array
+/home/revision-6/bin/cpi:11> declare -a array
array=$(yad --width=600 --height=800 \
--title="Choose files to move." \
--file --multiple --separator='\n')
+/home/revision-6/bin/cpi:12> array=+/home/revision-6/bin/cpi:12> yad '--width=600' '--height=800' '--title=Choose files to move.' --file --multiple '--separator=\n'
(yad:3962282): dbind-WARNING **: 15:34:42.076: Couldn't connect to accessibility bus: Failed to connect to socket /root/.cache/at-spi/bus_0.0: Permission denied
+/home/revision-6/bin/cpi:12> array=$'/home/revision-6/Videos/betty_boop_a_song_a_day_1936.mkv\n/home/revision-6/Videos/betty_boop_is_my_palm_read_1932.mkv\n/home/revision-6/Videos/betty_boop_more_pep_1936.mkv'
echo $array
+/home/revision-6/bin/cpi:15> echo $'/home/revision-6/Videos/betty_boop_a_song_a_day_1936.mkv\n/home/revision-6/Videos/betty_boop_is_my_palm_read_1932.mkv\n/home/revision-6/Videos/betty_boop_more_pep_1936.mkv'
/home/revision-6/Videos/betty_boop_a_song_a_day_1936.mkv
/home/revision-6/Videos/betty_boop_is_my_palm_read_1932.mkv
/home/revision-6/Videos/betty_boop_more_pep_1936.mkv
target=$(yad --width=600 --height=800 \
--title="Choose destination." \
--file --directory)
+/home/revision-6/bin/cpi:17> target=+/home/revision-6/bin/cpi:17> yad '--width=600' '--height=800' '--title=Choose destination.' --file --directory
(yad:3962312): dbind-WARNING **: 15:34:54.949: Couldn't connect to accessibility bus: Failed to connect to socket /root/.cache/at-spi/bus_0.0: Permission denied
+/home/revision-6/bin/cpi:17> target=/home/revision-6/video_processing
echo "target="$target
+/home/revision-6/bin/cpi:20> echo 'target=/home/revision-6/video_processing'
target=/home/revision-6/video_processing
# The file you wish to move may already exist in the target directory.
# If you do not want to overwrite it you can use
# mv -n
# mv --no-clobber
# or
# mv --backup=numbered
# Clumsy, but better than nothing, and doesn't give me what I want.
# That is why I wrote this script, it adds an incremented number to the name.
# eg <movie>.<ext>
# eg <movie>_copy1.<ext>
# eg <movie>_copy2.<ext>
# Yes, I could use brackets but they are illegal characters and I have a renamer
# that would remove them.
# You can't just use <filename>.$ext to check if the file exists in target because
# you'll miss any existing copies. You have to account for iteration.
for filename in $array; do
unset x
# decompose filename
# just name without path
filename=$filename:t
echo
echo
echo "filename="$filename
echo
echo
# name without extension
shortname=${filename%.*}
echo "shortname="$shortname
echo
echo
# extension
ext=${filename##*.}
echo
echo
echo "ext="$ext
# The key here is to rename the file (if necessary) BEFORE copying it to target instead
# of after.
# Does file already exist in $target? Count them.
x=$(find $target -iname "$shortname*.$ext" | wc -c)
echo
echo
echo "x="$x
# If no file exists, move to target.
# If one file exists then $x will return "1". Think of it as
# <filename>_copy0.<ext>,
# this makes the file you wish to move
# <filename>_copy1.<ext>, - the value of $x.
case $x in
0) cp $filename $target
sleep .2
;;
*) mv $filename $shortname_copy$x.$ext
cp $filename $target
sleep .2
;;
esac
done
+/home/revision-6/bin/cpi:38> filename=/home/revision-6/Videos/betty_boop_a_song_a_day_1936.mkv
/home/revision-6/Videos/betty_boop_is_my_palm_read_1932.mkv
/home/revision-6/Videos/betty_boop_more_pep_1936.mkv
+/home/revision-6/bin/cpi:39> unset x
+/home/revision-6/bin/cpi:42> filename=betty_boop_more_pep_1936.mkv
+/home/revision-6/bin/cpi:43> echo
+/home/revision-6/bin/cpi:44> echo
+/home/revision-6/bin/cpi:45> echo 'filename=betty_boop_more_pep_1936.mkv'
filename=betty_boop_more_pep_1936.mkv
+/home/revision-6/bin/cpi:46> echo
+/home/revision-6/bin/cpi:47> echo
+/home/revision-6/bin/cpi:49> shortname=betty_boop_more_pep_1936
+/home/revision-6/bin/cpi:50> echo 'shortname=betty_boop_more_pep_1936'
shortname=betty_boop_more_pep_1936
+/home/revision-6/bin/cpi:51> echo
+/home/revision-6/bin/cpi:52> echo
+/home/revision-6/bin/cpi:54> ext=mkv
+/home/revision-6/bin/cpi:55> echo
+/home/revision-6/bin/cpi:56> echo
+/home/revision-6/bin/cpi:57> echo 'ext=mkv'
ext=mkv
+/home/revision-6/bin/cpi:62> x=+/home/revision-6/bin/cpi:62> find /home/revision-6/video_processing -iname 'betty_boop_more_pep_1936*.mkv'
+/home/revision-6/bin/cpi:62> x=+/home/revision-6/bin/cpi:62> wc -c
+/home/revision-6/bin/cpi:62> x=0
+/home/revision-6/bin/cpi:63> echo
+/home/revision-6/bin/cpi:64> echo
+/home/revision-6/bin/cpi:65> echo 'x=0'
x=0
+/home/revision-6/bin/cpi:73> case 0 (0)
+/home/revision-6/bin/cpi:74> cp betty_boop_more_pep_1936.mkv /home/revision-6/video_processing
+/home/revision-6/bin/cpi:75> sleep .2
exit 0
+/home/revision-6/bin/cpi:83> exit 0
revision-6@revision6-NYI3 ~/Videos
% cpi
+/home/revision-6/bin/cpi:4> setopt -v
autoload -Uz zmv
+/home/revision-6/bin/cpi:5> autoload -Uz zmv
# WARNING Just because two or more files from different sources have identical names does not mean
#that the files are identical.
declare -a array
+/home/revision-6/bin/cpi:11> declare -a array
array=$(yad --width=600 --height=800 \
--title="Choose files to move." \
--file --multiple --separator='\n')
+/home/revision-6/bin/cpi:12> array=+/home/revision-6/bin/cpi:12> yad '--width=600' '--height=800' '--title=Choose files to move.' --file --multiple '--separator=\n'
(yad:3962420): dbind-WARNING **: 15:35:35.029: Couldn't connect to accessibility bus: Failed to connect to socket /root/.cache/at-spi/bus_0.0: Permission denied
+/home/revision-6/bin/cpi:12> array=$'/home/revision-6/Videos/betty_boop_a_song_a_day_1936.mkv\n/home/revision-6/Videos/betty_boop_is_my_palm_read_1932.mkv\n/home/revision-6/Videos/betty_boop_more_pep_1936.mkv'
echo $array
+/home/revision-6/bin/cpi:15> echo $'/home/revision-6/Videos/betty_boop_a_song_a_day_1936.mkv\n/home/revision-6/Videos/betty_boop_is_my_palm_read_1932.mkv\n/home/revision-6/Videos/betty_boop_more_pep_1936.mkv'
/home/revision-6/Videos/betty_boop_a_song_a_day_1936.mkv
/home/revision-6/Videos/betty_boop_is_my_palm_read_1932.mkv
/home/revision-6/Videos/betty_boop_more_pep_1936.mkv
target=$(yad --width=600 --height=800 \
--title="Choose destination." \
--file --directory)
+/home/revision-6/bin/cpi:17> target=+/home/revision-6/bin/cpi:17> yad '--width=600' '--height=800' '--title=Choose destination.' --file --directory
(yad:3962450): dbind-WARNING **: 15:35:43.654: Couldn't connect to accessibility bus: Failed to connect to socket /root/.cache/at-spi/bus_0.0: Permission denied
+/home/revision-6/bin/cpi:17> target=/home/revision-6/video_processing
echo "target="$target
+/home/revision-6/bin/cpi:20> echo 'target=/home/revision-6/video_processing'
target=/home/revision-6/video_processing
# The file you wish to move may already exist in the target directory.
# If you do not want to overwrite it you can use
# mv -n
# mv --no-clobber
# or
# mv --backup=numbered
# Clumsy, but better than nothing, and doesn't give me what I want.
# That is why I wrote this script, it adds an incremented number to the name.
# eg <movie>.<ext>
# eg <movie>_copy1.<ext>
# eg <movie>_copy2.<ext>
# Yes, I could use brackets but they are illegal characters and I have a renamer
# that would remove them.
# You can't just use <filename>.$ext to check if the file exists in target because
# you'll miss any existing copies. You have to account for iteration.
for filename in $array; do
unset x
# decompose filename
# just name without path
filename=$filename:t
echo
echo
echo "filename="$filename
echo
echo
# name without extension
shortname=${filename%.*}
echo "shortname="$shortname
echo
echo
# extension
ext=${filename##*.}
echo
echo
echo "ext="$ext
# The key here is to rename the file (if necessary) BEFORE copying it to target instead
# of after.
# Does file already exist in $target? Count them.
x=$(find $target -iname "$shortname*.$ext" | wc -c)
echo
echo
echo "x="$x
# If no file exists, move to target.
# If one file exists then $x will return "1". Think of it as
# <filename>_copy0.<ext>,
# this makes the file you wish to move
# <filename>_copy1.<ext>, - the value of $x.
case $x in
0) cp $filename $target
sleep .2
;;
*) mv $filename $shortname_copy$x.$ext
cp $filename $target
sleep .2
;;
esac
done
+/home/revision-6/bin/cpi:38> filename=/home/revision-6/Videos/betty_boop_a_song_a_day_1936.mkv
/home/revision-6/Videos/betty_boop_is_my_palm_read_1932.mkv
/home/revision-6/Videos/betty_boop_more_pep_1936.mkv
+/home/revision-6/bin/cpi:39> unset x
+/home/revision-6/bin/cpi:42> filename=betty_boop_more_pep_1936.mkv
+/home/revision-6/bin/cpi:43> echo
+/home/revision-6/bin/cpi:44> echo
+/home/revision-6/bin/cpi:45> echo 'filename=betty_boop_more_pep_1936.mkv'
filename=betty_boop_more_pep_1936.mkv
+/home/revision-6/bin/cpi:46> echo
+/home/revision-6/bin/cpi:47> echo
+/home/revision-6/bin/cpi:49> shortname=betty_boop_more_pep_1936
+/home/revision-6/bin/cpi:50> echo 'shortname=betty_boop_more_pep_1936'
shortname=betty_boop_more_pep_1936
+/home/revision-6/bin/cpi:51> echo
+/home/revision-6/bin/cpi:52> echo
+/home/revision-6/bin/cpi:54> ext=mkv
+/home/revision-6/bin/cpi:55> echo
+/home/revision-6/bin/cpi:56> echo
+/home/revision-6/bin/cpi:57> echo 'ext=mkv'
ext=mkv
+/home/revision-6/bin/cpi:62> x=+/home/revision-6/bin/cpi:62> find /home/revision-6/video_processing -iname 'betty_boop_more_pep_1936*.mkv'
+/home/revision-6/bin/cpi:62> x=+/home/revision-6/bin/cpi:62> wc -c
+/home/revision-6/bin/cpi:62> x=63
+/home/revision-6/bin/cpi:63> echo
+/home/revision-6/bin/cpi:64> echo
+/home/revision-6/bin/cpi:65> echo 'x=63'
x=63
+/home/revision-6/bin/cpi:73> case 63 (0)
+/home/revision-6/bin/cpi:73> case 63 (*)
+/home/revision-6/bin/cpi:77> mv betty_boop_more_pep_1936.mkv 63.mkv
+/home/revision-6/bin/cpi:78> cp betty_boop_more_pep_1936.mkv /home/revision-6/video_processing
cp: cannot stat 'betty_boop_more_pep_1936.mkv': No such file or directory
+/home/revision-6/bin/cpi:79> sleep .2
exit 0
+/home/revision-6/bin/cpi:83> exit 0
在我看来,这个逻辑似乎有效。在移动文件之前,计算目标中的迭代次数并重命名文件,但我想我的 shell 脚本编写能力不足以胜任这项任务。欢迎任何帮助。
zsh
使用s 强大的通配符语法代替find
and可能会更容易wc
。试试这个:关键声明如下:
它从目标目录中创建与源文件名的模式匹配的排序文件名数组。
${fqp:r}
- 文件路径的根目录。它包含目录和名称的前导部分,即除扩展名之外的所有内容。(_copy<->|)
-_copyN
,或者什么都没有(例如fname.ext
或fname_copy14.ext
)。<->
是任意数字的 glob 模式。${fqp:e}
- 文件扩展名。(#qNn)
- 一组修改行为的 glob 限定符#q
- 表示这个括号内的表达式包含 glob 限定符。N
- 允许空 glob,这样零匹配就不会出现错误。n
- 为该扩展启用 NUMERIC_GLOB_SUBST,以便结果按数字排序。使用此选项,一组名称(如)f1
f11
f2
将按排序f1
f2
f11
。同一位置没有数字的文件名(如fname.ext
和)fname_copy1.ext
仍将按词汇顺序排序,但这是我们想要的顺序,因此不需要特殊处理。对数组进行排序后,数组中的最后一个值 (index
-1
) 将是编号最高的文件名,因此我们可以使用它来确定下一步该做什么。使用这种技术的一个优点是它可以处理编号中的间隙;基于计数的算法可能会因此而出错。使用/测试:
我们可以使用此函数来构建一个循环遍历文件名数组的脚本: