从一个Shell脚本中移动包含空格的文件

6

我正在试图使用mv命令重命名文件(将空格替换为破折号)。如果我在不使用引号的情况下传递源文件和目标文件参数,由于mv认为这是参数结束,所以源文件会被分解。

如果我用引号包围源文件和目标文件参数,mv会报告找不到源文件。目前,$1和$2在开头和结尾都有硬编码引号,在没有硬编码引号的情况下,它们的等价项是$onam和$dnam。

从提示符中运行的此命令完美地工作,因为源文件和目标文件都用引号括起来:

mv "/home/bryan/renametest/TestFolder/test file" "/home/bryan/renametest/TestFolder/test---file"

这是我目前尝试过的(输入和结果):
mv $1 $2
mv: cannot stat `"/home/bryan/renametest/TestFolder/test   file"'
   No such file or directory

mv $onam $dnam
mv: cannot move `/home/bryan/renametest/TestFolder' to a subdirectory of itself, 
  `/home/bryan/renametest/TestFolder/test---file'

mv $1 $dnam
mv: cannot stat `"/home/bryan/renametest/TestFolder/test   file"': 
  No such file or directory

mv $onam $2
mv: cannot move `/home/bryan/renametest/TestFolder' to `"/home/bryan/renametest/TestFolder/test---file"': 
  No such file or directory

我该如何移动那些包含空格的文件?

请问有没有Bash脚本可以将文件名中的空格替换成下划线? - jsedano
正如在您其他相关问题的评论中建议的那样,将引号从变量内容中取出。否则,shell将寻找名称中带有引号的文件。 - tink
目前,$1和$2的开头和结尾都有硬编码引号。$onam和$dnam是等效的没有硬编码引号的变量。 - user2021539
1个回答

6

使用:

mv "$onam" "$dnam"

基本上,对于包含可能包含空格的文件名的变量,请在其周围使用引号。但是,这些值本身不应该包含引号,除非文件名或目录名本身包含引号。

不幸的是,我无法立即想到一种简单的方法来使用带有硬编码引号的值,例如$1。您必须使用eval或类似方法进行操作。


一个SSCCE(短小精悍的,自给自足的,正确的示例

此代码与您展示的代码是同构的,但实际上做了更多的事情。它也可以安全运行并在其后清理自己。它假定您正在使用bash。请注意,包含空格的名称包含多个连续的空格; 如果尝试使用包含单个空格的名称,则很容易产生错误的安全感。

#!/bin/bash
testdir=test.$$
mkdir "$testdir" || exit 1

trap "rm -fr '$testdir'; exit 1" 0 1 2 3 13 15

(
    set -x
    x=$'\n\n'
    cd test.$$

    basedir="$PWD"
    sub1dir="rename  test"
    sub2dir="Test   Folder"
    subdir="$sub1dir/$sub2dir"

    mkdir -p "$basedir/$subdir"

    oldfile="test   file"

    cp /etc/group "$basedir/$subdir/$oldfile"

    echo "$x"
    ls -lR .

    echo "$x"
    newfile=$(echo "$oldfile" | sed 's/ /-/g')
    mv "$basedir/$subdir/$oldfile" "$basedir/$subdir/$newfile"
    ls -lR .

    echo "$x"
    new2dir=$(echo "$sub2dir" | sed 's/ /-/g')
    mv "$basedir/$subdir"  "$basedir/$sub1dir/$new2dir"
    ls -lR .

    echo "$x"
    new1dir=$(echo "$sub1dir" | sed 's/ /-/g')
    mv "$basedir/$sub1dir" "$basedir/$new1dir"
    ls -lR .
)

rm -fr "$testdir"
trap 0

#    $ mv "/home/bryan/renametest/TestFolder/test   file" "/home/bryan/renametest/TestFolder/test---file"
#
#Here are some inputs and results.
#
#    mv $1 $2
#    mv: cannot stat `"/home/bryan/renametest/TestFolder/test   file"': No such file or directory
#
#    mv $onam $dnam
#    mv: cannot move `/home/bryan/renametest/TestFolder' to a subdirectory of itself, `/home/bryan/renametest/TestFolder/test---file'
#
#    mv $1 $dnam
#    mv: cannot stat `"/home/bryan/renametest/TestFolder/test   file"': No such file or directory
#
#    mv $onam $2
#    mv: cannot move `/home/bryan/renametest/TestFolder' to `"/home/bryan/renametest/TestFolder/test---file"': No such file or directory
#
#Ideas?

需要小心;注意我如何从碎片中构建和分解名称等。但是小心谨慎可以做到。

示例输出

使用bash -x运行,但您也可以只使用bash运行,您将只跳过少量的输出,因为它包含在子shell代码中的set -x

+ testdir=test.47648
+ mkdir test.47648
+ trap 'rm -fr '\''test.47648'\''; exit 1' 0 1 2 3 13 15
+ set -x
+ x='

'
+ cd test.47648
+ basedir=/Users/jleffler/tmp/soq/x3/test.47648
+ sub1dir='rename  test'
+ sub2dir='Test   Folder'
+ subdir='rename  test/Test   Folder'
+ mkdir -p '/Users/jleffler/tmp/soq/x3/test.47648/rename  test/Test   Folder'
+ oldfile='test   file'
+ cp /etc/group '/Users/jleffler/tmp/soq/x3/test.47648/rename  test/Test   Folder/test   file'
+ echo '

'



+ ls -lR .
total 0
drwxr-xr-x  3 jleffler  staff  102 Apr  3 18:43 rename  test

./rename  test:
total 0
drwxr-xr-x  3 jleffler  staff  102 Apr  3 18:43 Test   Folder

./rename  test/Test   Folder:
total 8
-rw-r--r--  1 jleffler  staff  2151 Apr  3 18:43 test   file
+ echo '

'



++ echo 'test   file'
++ sed 's/ /-/g'
+ newfile=test---file
+ mv '/Users/jleffler/tmp/soq/x3/test.47648/rename  test/Test   Folder/test   file' '/Users/jleffler/tmp/soq/x3/test.47648/rename  test/Test   Folder/test---file'
+ ls -lR .
total 0
drwxr-xr-x  3 jleffler  staff  102 Apr  3 18:43 rename  test

./rename  test:
total 0
drwxr-xr-x  3 jleffler  staff  102 Apr  3 18:43 Test   Folder

./rename  test/Test   Folder:
total 8
-rw-r--r--  1 jleffler  staff  2151 Apr  3 18:43 test---file
+ echo '

'



++ echo 'Test   Folder'
++ sed 's/ /-/g'
+ new2dir=Test---Folder
+ mv '/Users/jleffler/tmp/soq/x3/test.47648/rename  test/Test   Folder' '/Users/jleffler/tmp/soq/x3/test.47648/rename  test/Test---Folder'
+ ls -lR .
total 0
drwxr-xr-x  3 jleffler  staff  102 Apr  3 18:43 rename  test

./rename  test:
total 0
drwxr-xr-x  3 jleffler  staff  102 Apr  3 18:43 Test---Folder

./rename  test/Test---Folder:
total 8
-rw-r--r--  1 jleffler  staff  2151 Apr  3 18:43 test---file
+ echo '

'



++ echo 'rename  test'
++ sed 's/ /-/g'
+ new1dir=rename--test
+ mv '/Users/jleffler/tmp/soq/x3/test.47648/rename  test' /Users/jleffler/tmp/soq/x3/test.47648/rename--test
+ ls -lR .
total 0
drwxr-xr-x  3 jleffler  staff  102 Apr  3 18:43 rename--test

./rename--test:
total 0
drwxr-xr-x  3 jleffler  staff  102 Apr  3 18:43 Test---Folder

./rename--test/Test---Folder:
total 8
-rw-r--r--  1 jleffler  staff  2151 Apr  3 18:43 test---file
+ rm -fr test.47648
+ trap 0

mv "$onam" "$dnam" mv: 无法将“/home/bryan/renametest/TestFolder”移动到其子目录“/home/bryan/renametest/TestFolder/test---file”中。 - user2021539
$onam=/home/bryan/renametest/TestFolder/test 文件 - user2021539
那是一个单独的问题。你显然正在尝试将一个文件夹移动到一个新位置,而这个新位置是当前位置的子目录。你展示的错误信息与$onam的声明值不一致。在你的问题更新中展示sh -x yourscript的输出以及你提供的任何命令,这样你就可以正确地格式化输出。 - Jonathan Leffler
1
你可以使用Bash变量替换而不是sed将空格更改为破折号 - onam="the file"; echo ${onam// /-} -> the--file - evil otto
1
@evilotto:是啊,但老习惯难改,25年甚至更长时间的老习惯需要花费很大的力气去改变。 - Jonathan Leffler
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接