带有空格/ chown镜像的文件名的Bash脚本

时间:2017-05-26 01:52:59

标签: bash shell

我的文件结构因故障软件的所有权更改而损坏。我有一个几乎完全相同的结构,来自之前的备份。

我想要完成的是镜像文件所有权,以解决此问题。

我有以下脚本,但由于文件名/目录上的空格而失败:

IFS=$'\n'
D1=/Users/emarques/temp/test\ 1
D2=/Users/emarques/temp/test\ 2
for entry in $(find $D1  -exec stat -f "%N:%u:%g" {} \;); do 
    $(echo \"$entry\" | \
        tr -d '"' | \
        sed 's#'$D1'#'$D2'#' | \
        awk -F: '{printf ("chown %s %s\n", $2 ":" $3, "\"" $1 "\"")}')
done

输出

-bash: chown 0:20 "/Users/emarques/temp/test 2": No such file or directory
-bash: chown 501:20 "/Users/emarques/temp/test 2/1": No such file or directory
-bash: chown 501:20 "/Users/emarques/temp/test 2/1/a": No such file or directory
-bash: chown 501:20 "/Users/emarques/temp/test 2/1/b": No such file or directory
-bash: chown 501:20 "/Users/emarques/temp/test 2/2": No such file or directory
-bash: chown 501:20 "/Users/emarques/temp/test 2/2/a": No such file or directory
-bash: chown 502:20 "/Users/emarques/temp/test 2/2/b": No such file or directory
-bash: chown 501:20 "/Users/emarques/temp/test 2/3": No such file or directory
-bash: chown 502:80 "/Users/emarques/temp/test 2/a": No such file or directory
-bash: chown 502:80 "/Users/emarques/temp/test 2/b": No such file or directory
-bash: chown 501:20 "/Users/emarques/temp/test 2/c": No such file or directory

我向IT同事请求帮助,因为我还没弄清楚如何完成这项工作。任何意见都非常感谢。

2 个答案:

答案 0 :(得分:2)

某些版本的chown支持“参考文件”选项:

--reference=RFILE
      use  RFILE's  owner and group rather than specifying OWNER:GROUP
      values

如果您的chown有,请执行:

cd "/Users/emarques/temp/test 1"
find . -exec sh -c 'chown --reference="$1" "/Users/emarques/temp/test 2/$1"' _ {}

或者,稍微复杂一点,但可能更有效:

cd "/Users/emarques/temp/test 1"
find . -exec sh -c 'for f; do chown --reference="$f" "/Users/emarques/temp/test 2/$f"; done' _ {} +

你不必再担心引用了。

如果您的chown不支持参考文件,则:

cd "/Users/emarques/temp/test 1"
shopt -s globstar dotglob
for f in **/*
do
    chown "$(stat -f "%u:%g" "$f")" "/Users/emarques/temp/test 2/$f"
done

从第一个目录开始,你可以省去很多麻烦,因为你可以免费获得相对路径。

答案 1 :(得分:2)

stat命令的选项和用户目录的结构,我怀疑你在macOS中运行它,这意味着你可能只限于bash v3和你的工具(比如stat)和chown)使用BSD选项。

乍一看,这里有一些你不应该做的事情。

  • 您需要引用其余变量。您在$D1中添加了一个空格,但随后是find $D1而不是find "$D1"。请注意,echo \"$entry\"包含一个未加引号的变量。你需要引用变量的地方是它们可能被空格分割的任何地方。由于有时候很难知道,所以到处都引用它们。
  • 当您for entry in $(find ...)并且find的结果包含空格时,您不太可能获得所需的结果。有关原因的说明,请查看ParsingLs页面。 (您正试图通过设置$IFS来缓解此问题,但这会使其他事情变得复杂。)

除此之外,您可以使用多种方法之一。

1 - 在find中运行所有内容。

您已了解find命令的-exec选项。您可以在find中加载更多内容。以下是未经测试的:

cd "$D1"
find . -exec sh -c 'chown $(stat -f "%u:%g" {}) $0/{}' "$D2" \;

2 - 使用更好的分隔符。

find命令有-print0选项,导致文件由空值而不是空格分隔。这也是未经测试的:

find "$D1" -print0 | while read -d$'\0' file; do
  chown $(stat -f '%u:%g' "$file") "$D2/$file"
done

done

3 - 如果可以,请避免查找,使用globstar。

这仅适用于bash 4可用,因为bash 3(macOS中的默认值)不包含globstar。 (如果需要,您可以使用macportshomebrew安装bash 4.)

D1="/Users/emarques/temp/test 1"
D2="/Users/emarques/temp/test 2"

shopt -s globstar

# If there are files in D2 that might not appear in D1...
cd "$D1"
for file in **; do
  chown $(stat -f '%u:%g' "$file") "$D2/$file"
done

# Or if there are files in D1 which might be missing from D2...
cd "$D2"
for file in **; do
  chown $(stat -f '%u:%g' "$D1/$file") "$file"
done

这里有两个起点,以防文件列表完全匹配。如果它们匹配,则两个选项应该是等效的。