bash Linux:创建随机目录/文件层次结构

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13400312/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 03:48:04  来源:igfitidea点击:

Linux: create random directory/file hierarchy

linuxbashshellcommand-line-interface

提问by Andreas Gohr

For testing a tool I need a directory with a whole bunch of different Office files in a deep nested structure. I already have the files in a directory, but now need to create some random nested sub directories and spread out the files in them.

为了测试一个工具,我需要一个目录,其中包含一大堆不同的 Office 文件,它们具有深层嵌套结构。我已经将文件放在一个目录中,但现在需要创建一些随机嵌套的子目录并将其中的文件展开。

I could sit down and write a proper program in a programming language of my choice, but I wonder if there might be a clever combination of Linux command line tools + Bash to achieve what I want.

我可以坐下来用我选择的编程语言编写一个合适的程序,但我想知道是否有 Linux 命令行工具 + Bash 的巧妙组合来实现我想要的。

Edit: to clarify, my input is a directory with a about 200 files. The output should be a directory hierarchy containing these files more or less evenly spread. Directory names should be more than single letters, vary randomly in length and use various allowed characters (utf-8 filesystem).

编辑:澄清一下,我的输入是一个包含大约 200 个文件的目录。输出应该是包含这些文件或多或少均匀分布的目录层次结构。目录名称不应超过单个字母,长度随机变化并使用各种允许的字符(utf-8 文件系统)。

采纳答案by Andreas Gohr

I wasn't too happy with the given answers, so I came up with my own. The following takes my input files and uses /dev/urandom to gather 10 to 256 printable chars, puts in a few more directory separators, creates the directory hierarchy and places a file in it.

我对给出的答案不太满意,所以我想出了自己的答案。下面获取我的输入文件并使用 /dev/urandom 收集 10 到 256 个可打印字符,放入更多的目录分隔符,创建目录层次结构并在其中放置一个文件。

Using urandom creates some really weird directory names which is good for my purpose. I'm sure a real Unix guru could simplify this even more. The dir building could probably be done in a single awk command for example.

使用 urandom 会创建一些非常奇怪的目录名称,这对我的目的很有用。我确信真正的 Unix 大师可以进一步简化这一点。例如,目录构建可能可以在单个 awk 命令中完成。

#!/bin/bash
INDIR='files';

IFS=$'\n'
for FILE in `ls $INDIR/*`; do
    DIR=`cat /dev/urandom | \
         tr -dc '[ -~]' | \
         tr 'ABCDEF\\' '///////' | \
         head -c$((10 + $RANDOM % 256))`

    mkdir -p $DIR
    cp $FILE $DIR
done

回答by kev

You can use bash brace-expansion:

您可以使用 bash brace-expansion

mkdir -p {a,b}/{e,f,g}/{h,i,j}


├───a
│   ├───e
│   │   ├───h
│   │   ├───i
│   │   └───j
│   ├───f
│   │   ├───h
│   │   ├───i
│   │   └───j
│   └───g
│       ├───h
│       ├───i
│       └───j
└───b
    ├───e
    │   ├───h
    │   ├───i
    │   └───j
    ├───f
    │   ├───h
    │   ├───i
    │   └───j
    └───g
        ├───h
        ├───i
        └───j

回答by Gilles Quenot

This is a script that generate a random dir structure :

这是一个生成随机目录结构的脚本:

#!/bin/bash

# Decimal ASCII codes (see man ascii)
ARR=( {48..57} {65..90} {97..122} )

# Array count
arrcount=${#ARR[@]}

# return a random string
get_rand_dir(){
    for ((i=1; i<$((RANDOM%30)); i++)) {
        printf \$(printf '%03o' ${ARR[RANDOM%arrcount]});
    }
}

dir=/tmp/

# appending random characters to make a hierarchy
for ((i=0; i<$((RANDOM%100)); i++)) {
    dir+="$(get_rand_dir)/"
}

echo $dir
mkdir -p "$dir"

oldir=$(echo "$dir" | cut -d '/' -f1-3)

while [[ $dir ]]; do
    dir=${dir%/*}
    cd $dir
    for ((i=0; i<$((RANDOM%100)); i++)) {
        mkdir &>/dev/null -p $(get_rand_dir)
    }
done

tree "$oldir"

OUTPUT

输出

/tmp/x
├── egeDVPW
├── iOkr
├── l
├── o1gye8uF
├── q
│?? ├── 4Dlrfagv
│?? ├── 4Yxmoqf
│?? ├── 8LkyIrXA
│?? ├── 8m9kse8s
│?? ├── aV
│?? ├── in
│?? │?? ├── 12zdLso68HWlPK
│?? │?? │?? ├── C
│?? │?? │?? ├── DOYt8wUW
│?? │?? │?? ├── FXP
│?? │?? │?? ├── hFLem8
│?? │?? │?? ├── hhHIv
│?? │?? │?? ├── iD87kxs54x04
│?? │?? │?? ├── oFM
│?? │?? │?? ├── OjFT

Now you can create an array of dirs :

现在您可以创建一个 dirs 数组:

shopt -s globstar # require bash4
dirs=( /tmp/x/** )
printf '%s\n' ${dirs[@]}

and populate dirs with files randomly. You have enough examples to do so. I've done the most hard work.

并用文件随机填充目录。你有足够的例子来做到这一点。我做了最辛苦的工作。

回答by sdaau

Thanks to all who posted here; it turns out, it wasn't really trivial to escape filenames with special characters, so I built my own script based on those here; here is how it behaves with special character filenames:

感谢所有在这里发帖的人;事实证明,用特殊字符转义文件名并不是一件容易的事,所以我基于这里的那些构建了自己的脚本;这是它对特殊字符文件名的行为方式:

$ ~/rndtree.sh ./rndpath 0 3 1
Warning: will create random tree at: ./rndpath
Proceed (y/n)? y
Removing old outdir ./rndpath
mkdir -p ./rndpath/";"/{")?DxVBBJ{w2","L,|+","^VC)Vn.6!"}/"D+,IFJ( LN"
> > > > > > > > > > > 
./rndpath
└── [       4096]  ;
    ├── [       4096]  )?DxVBBJ{w2
    │?? ├── [       4096]  D+,IFJ( LN
    │?? │?? └── [        929]  r2.bin
    │?? ├── [       8557]  %3fsaG# Rl;ffXf.bin
    │?? └── [      19945]  Dzk .bin
    ├── [       4096]  L,|+
    │?? ├── [       4096]  D+,IFJ( LN
    │?? │?? ├── [       2325]  6Qg#pe5j'&ji49oqrO.bin
    │?? │?? ├── [      16345]  #?.bin
    │?? │?? └── [       2057]  Uz-0XtLVWz#}0lI.bin
    │?? ├── [       2543]  bbtA-^s22vdTu.bin
    │?? └── [      10848]  K46+kh7L9.bin
    ├── [       4096]  ^VC)Vn.6!
    │?? ├── [       4096]  D+,IFJ( LN
    │?? ├── [      10502]  8yY,MqZ ^5+_SA^.r4{.bin
    │?? └── [      17628]  ipO"|69.bin
    └── [      12376]  a2Y% }G1.qDir.bin

7 directories, 11 files
total bytes: 136823 ./rndpath

and here with a safe subset of ASCII:

这里有一个安全的 ASCII 子集:

$ ~/rndtree.sh ./rndpath 1 3 1
Warning: will create random tree at: ./rndpath
Proceed (y/n)? y
Removing old outdir ./rndpath
mkdir -p ./rndpath/"48SLS"/{"nyG","jIC6vj"}/{"PSLd5tMn","V R"}
> > > > > > > 
./rndpath
├── [       4096]  48SLS
│?? ├── [       4096]  jIC6vj
│?? │?? ├── [       4096]  PSLd5tMn
│?? │?? ├── [       4096]  V R
│?? │?? │?? ├── [        922]  lg.bin
│?? │?? │?? └── [       9600]  VVYG.bin
│?? │?? ├── [      10883]  B7nt06p.bin
│?? │?? └── [      19339]  g5uT i.bin
│?? ├── [       4096]  nyG
│?? │?? ├── [       4096]  PSLd5tMn
│?? │?? └── [       4096]  V R
│?? │??     └── [       6128]  1tfLR.bin
│?? └── [       5448]  Jda.bin
└── [      18196]  KwEXu2O2H9s.bin

Spaces should be handled in both cases - however, note that subdirectory names repeat (while filenames do not).

在这两种情况下都应该处理空格 - 但是,请注意子目录名称重复(而文件名不重复)。

The script rndtree.sh:

脚本rndtree.sh

#!/usr/bin/env bash

# http://stackoverflow.com/questions/13400312/linux-create-random-directory-file-hierarchy
# Decimal ASCII codes (see man ascii); added space
AARR=( 32 {48..57} {65..90} {97..122} )
# Array count
aarrcount=${#AARR[@]}

if [ "" == "" ] ; then
  OUTDIR="./rndpath" ;
else
  OUTDIR="" ;
fi

if [ "" != "" ] ; then
  ASCIIONLY="" ;
else
  ASCIIONLY=1 ;
fi

if [ "" != "" ] ; then
  DIRDEPTH="" ;
else
  DIRDEPTH=3 ;
fi

if [ "" != "" ] ; then
  MAXFIRSTLEVELDIRS="" ;
else
  MAXFIRSTLEVELDIRS=2 ;
fi

if [ "" != "" ] ; then
  MAXDIRCHILDREN="" ;
else
  MAXDIRCHILDREN=4 ;
fi

if [ "" != "" ] ; then
  MAXDIRNAMELEN="" ;
else
  MAXDIRNAMELEN=12 ;
fi

if [ "" != "" ] ; then
  MAXFILECHILDREN="" ;
else
  MAXFILECHILDREN=4 ;
fi

if [ "" != "" ] ; then
  MAXFILENAMELEN="" ;
else
  MAXFILENAMELEN=20 ;
fi

if [ "" != "" ] ; then
  MAXFILESIZE="" ;
else
  MAXFILESIZE=20000 ;
fi

MINDIRNAMELEN=1
MINFILENAMELEN=1
MINDIRCHILDREN=1
MINFILECHILDREN=0
MINFILESIZE=1
FILEEXT=".bin"
VERBOSE=0 #1

get_rand_dirname() {
  if [ "$ASCIIONLY" == "1" ]; then
    for ((i=0; i<$((MINDIRNAMELEN+RANDOM%MAXDIRNAMELEN)); i++)) {
      printf \$(printf '%03o' ${AARR[RANDOM%aarrcount]});
    }
  else
    cat /dev/urandom | tr -dc '[ -~]' | tr -d '[$></~:`\]' | head -c$((MINDIRNAMELEN + RANDOM % MAXDIRNAMELEN)) | sed 's/\(["]\)/\/g'
  fi
  #echo -e " " # debug last dirname space
}

get_rand_filename() {
  if [ "$ASCIIONLY" == "1" ]; then
    for ((i=0; i<$((MINFILENAMELEN+RANDOM%MAXFILENAMELEN)); i++)) {
      printf \$(printf '%03o' ${AARR[RANDOM%aarrcount]});
    }
  else
    # no need to escape double quotes for filename
    cat /dev/urandom | tr -dc '[ -~]' | tr -d '[$></~:`\]' | head -c$((MINFILENAMELEN + RANDOM % MAXFILENAMELEN)) #| sed 's/\(["]\)/\/g'
  fi
  printf "%s" $FILEEXT
}


echo "Warning: will create random tree at: $OUTDIR"
[ "$VERBOSE" == "1" ] && echo "  MAXFIRSTLEVELDIRS $MAXFIRSTLEVELDIRS ASCIIONLY $ASCIIONLY DIRDEPTH $DIRDEPTH MAXDIRCHILDREN $MAXDIRCHILDREN MAXDIRNAMELEN $MAXDIRNAMELEN MAXFILECHILDREN $MAXFILECHILDREN MAXFILENAMELEN $MAXFILENAMELEN MAXFILESIZE $MAXFILESIZE"

read -p "Proceed (y/n)? " READANS
if [ "$READANS" != "y" ]; then
  exit
fi

if [ -d "$OUTDIR" ]; then
  echo "Removing old outdir $OUTDIR"
  rm -rf "$OUTDIR"
fi

mkdir "$OUTDIR"

if [ $MAXFIRSTLEVELDIRS -gt 0 ]; then
  NUMFIRSTLEVELDIRS=$((1+RANDOM%MAXFIRSTLEVELDIRS))
else
  NUMFIRSTLEVELDIRS=0
fi



# create directories
for (( ifl=0;ifl<$((NUMFIRSTLEVELDIRS));ifl++ )) {
  FLDIR="$(get_rand_dirname)"
  FLCHILDREN="";
  for (( ird=0;ird<$((DIRDEPTH-1));ird++ )) {
    DIRCHILDREN=""; MOREDC=0;
    for ((idc=0; idc<$((MINDIRCHILDREN+RANDOM%MAXDIRCHILDREN)); idc++)) {
      CDIR="$(get_rand_dirname)" ;
      # make sure comma is last, so brace expansion works even for 1 element? that can mess with expansion math, though
      if [ "$DIRCHILDREN" == "" ]; then DIRCHILDREN="\"$CDIR\"" ;
      else DIRCHILDREN="$DIRCHILDREN,\"$CDIR\"" ; MOREDC=1 ; fi
    }
    if [ "$MOREDC" == "1" ] ; then
      if [ "$FLCHILDREN" == "" ]; then FLCHILDREN="{$DIRCHILDREN}" ;
      else FLCHILDREN="$FLCHILDREN/{$DIRCHILDREN}" ; fi
    else
      if [ "$FLCHILDREN" == "" ]; then FLCHILDREN="$DIRCHILDREN" ;
      else FLCHILDREN="$FLCHILDREN/$DIRCHILDREN" ; fi
    fi
  }
  DIRCMD="mkdir -p $OUTDIR/\"$FLDIR\"/$FLCHILDREN"
  eval "$DIRCMD"
  echo "$DIRCMD"
}

# now loop through all directories, create random files inside
# note printf '%q' escapes to preserve spaces; also here
# escape, and don't wrap path parts in double quotes (e.g. | sed 's_/_"/"_g');
# note then we STILL have to eval to use it!
# but now ls "$D" works, so noneed for QD
# unfortunately backslashes can make '%q' barf - prevent them
find "$OUTDIR" -type d | while IFS= read D ; do
  QD="$(printf '%q' "$(echo "$D")" )" ;
  [ "$VERBOSE" == "1" ] && echo "$D"; #echo "$QD"; ls -la "$D"; #eval "ls -la $QD";
  for ((ifc=0; ifc<$((MINFILECHILDREN+RANDOM%MAXFILECHILDREN)); ifc++)) {
    CFILE="$(get_rand_filename)" ;
    echo -n '> '
    [ "$VERBOSE" == "1" ] && echo "$D"/"$CFILE"
    cat /dev/urandom \
    | head -c$((MINFILESIZE + RANDOM % MAXFILESIZE)) \
    > "$D"/"$CFILE"
  }
done

echo
tree -a --dirsfirst -s "$OUTDIR"
echo "total bytes: $(du -bs $(echo "$OUTDIR"))"