BASH 脚本 - 在变量中执行命令

BASH 脚本 - 在变量中执行命令

我正在编写一个 bash 脚本来每天备份我的数据库(在 Mac OS X 上),并且我想保存最新的 7 个备份目录。以下是我的脚本中存在问题的片段(用于列出除最新目录之外的所有内容):

# Assigning a value to a variable named NUMBEROFFOLDERS. 
NUMBEROFFOLDERS=7

# Command that leaves the newest NUMBEROFFOLDERS and removes everything else. 
TOBEDELETED=`(ls -t|head -n $(($NUMBEROFFOLDERS));ls)|sort|uniq -u|sed -e 's,.*,"&",g'|xargs rm -rfv`

# Executing the TOBEDELETED command. 
eval "$TOBEDELETED"

PS 当我在终端窗口中输入 TOBEDELETED 中指定的命令时,该命令有效,但当我运行脚本时什么也没有发生。


编辑:i)脚本应该以不同的方式列出特定目标中的所有目录两次;sort 应该解析它们,并且只有最新的目录才会从列表中删除;sed 应该正确处理所有空格,rm -rfv 将从列表中删除所有目录(要删除的目录)及其所有文件。

ii) 该脚本应该保留最新的$NUMBEROFFOLDERS 文件夹。

iii) 我没有收到错误。我相信问题出在我引用分配命令的变量时。

答案1

一个不太优雅的工作解决方案(检查你的环境:
让我们从一个分而治之方法,因为它更清晰:

ListToSave=$(ls -dt */ |head -n $NUMBEROFFOLDERS| cut -f1 -d'/';)

此行将要保存的文件夹列表存储在变量中ListToSave,也许稍后在脚本中可回收利用......

echo "$ListToSave"| awk 'BEGIN{printf("find . -maxdepth 1 -not -path \".\" ")}\
 {printf ("-not -path \"./"$0"\" ") } \
 END{print " -exec /bin/rm -rvf \"{}\" \\; "}' | /bin/sh

使用这个 3 行命令,您可以构造一个find命令来删除除要保存的目录之外的所有内容,然后将其通过管道传输到 shell(在本例中为/bin/sh)。
您可以检查命令构造,清除到 shell 的管道| /bin/sh
出于安全原因,使用完整路径运行命令总是更安全。(最好/bin/rmrm,只是为了避免一些木马)

评论:当您计划编写脚本时,解析输出是不安全的ls。通常情况下,您从有限且受控的情况开始;一段时间后,当您已经忘记它时,您应该尝试在更复杂且受控程度更低的情况下使用它。当您最终确信一切正常时......它发生了一些事情,而您意识到为时已晚。

笔记:

  1. 解析输出是不安全的ls...点击此处
  2. ls -dt */ | cut -f1 -d'/'应该只列出目录,甚至不列出文件。
  3. xargs立即执行命令行,正如您可以从其手册页中看到的那样

    xargs-从标准输入构建并执行命令行


我强烈建议避免做以下事情
但是如果你想解析,ls你可以从之前的尝试中写出与此类似的最小修改

TOBEDELETED=`(ls -dt */ |head -n $NUMBEROFFOLDERS | cut -f1 -d'/'; ls)| sort | uniq -u | xargs echo "rm -rfv"`

这样,您将拥有存储的输出,TOBEDELETED可以在编写脚本时稍后执行。

笔记: 它不适用于种类文件名如空格、、\t... \n,并且它可能会产生无法预料的副作用。假设您必须删除目录“1 bis”,但不能删除它所在的目录“1”。您将删除目录“1”,并且如果目录“bis”不存在,则在尝试删除目录“bis”时会出现错误。

答案2

你没有做你认为你在做的事情

# Command that leaves the newest NUMBEROFFOLDERS and removes everything else. 
TOBEDELETED=`(ls -t|head -n $(($NUMBEROFFOLDERS));ls)|sort|uniq -u|sed -e 's,.*,"&",g'|xargs rm -rfv`

反引号表示管道实际已执行,并且输出命令rm被捕获到变量中。此输出不不是包含命令,它只是rm -v输出。

我会简化一下这个命令:

stat -f "%m %N" * | sort -nr | sed -e "1,${numfolders}d" | cut -d " " -f 2 | xargs rm -rfv

我没有 BSD 系统可以玩,所以我不确定stat格式字符串是否正确。

答案3

我添加了另一个答案测试环境构造函数。我创建了一个此处共享可执行项目
按照链接在 shell 中写入bash main.sh。它起作用了。将脚本复制到您的系统上并检查它是否起作用。如果不行,请发布您收到的错误。

#!/bin/bash
printf "\n\n\n# Let's we create your test directory\n" 
TESTDIR="tmptmp"
    rm -rf $TESTDIR
    mkdir $TESTDIR
    cd $TESTDIR
      printf "\n### Now we are in \n"
        pwd 
        mkdir -p 1 2 3 4 5 6 7 8 9 "1 bis";
      printf "\n### Here the list in the creation order\n"
         ls -dt */
         sleep 1.5s             # Take your time ...
         touch 1 2 3 4 5 6 8;   # Make those the last modified
      printf "\n### Here the list with modified order\n"
         ls -dt */

printf "\n\n# Let's go with your script \n\n"

#
# It follows your script. You can put it where you prefer but not in the 
# directory that you want to delete else you can delete it too...
# If you put in the upper directory you can run it with: 
# /bin/bash ../myscript.sh
#
# If you do one time `chmod u+x myscript.sh` you can execute it with
# ../myscript 
# Remember to put as a 1st line "#!/bin/bash"
# Note: check the path of all the executable... 
#

#!/bin/bash
NUMBEROFFOLDERS=7
ListToSave=$(ls -dt */ |head -n $NUMBEROFFOLDERS| cut -f1 -d'/';)
printf "This is the list of directories to be saved:\n%s\n" "$ListToSave"

printf "\n# That's the command that you'll execute piping it into a shell\n"
echo "$ListToSave" \
     | awk 'BEGIN{printf("find . -maxdepth 1 -not -path \".\" ")}\
     {printf ("-not -path \"./"$0"\" ") } \
     END{print " -exec /bin/rm -rvf \"{}\" \\; "}' 
  sleep 1 ; 

printf "\n# NOW we execute those commands with \"the power of piping\" \n\n"
 echo "$ListToSave" \
      | awk 'BEGIN{printf("find . -maxdepth 1 -not -path \".\" ")}\
 {printf ("-not -path \"./"$0"\" ") } \
 END{print " -exec /bin/rm -rvf \"{}\" \\; "}' | /bin/sh


printf "\n# That's the list of the survived as 'ls -d *' says to us\n\n"
ls -d *
printf "\n\n ### It worked like a charm ###\n\n"

exit 0

注意:如果你用它执行bash -f main.sh它将不起作用,因为你会要求bash避免路径名扩展。在 中搜索man bash

相关内容