3

I have 5 empty directories:

[X]$ ls
dir_1  dir_2  dir_3  dir_4  dir_5

How can I make a text file in every one of these directories so that the text file would contain the name of the directory it is in? So for example, dir_1 would contain text file file_1.txt and this text file has text dir_1 in it, dir_2 contains text file file_2.txt that has text dir_2 in it and so on.

I searched for information but I couldn't figure this out. I started to learn linux very recently. Do I have to do something like:

find . -type d -exec touch (something?)

And is it possible to do this with for loops?

2 Answers2

4
for d in dir_*; do
    printf "%s\n" "$d" > "$d/file_${d##*_}.txt"
done
  • $d expands to each directory name.
  • ${d##*_} expands to the trailing number in the directory name, because it removes everything up to the last underscore from $d. Read more in Parameter Expansion section in BASH manual or POSIX specification.
  • printf "%s\n" "$d" > "$d/file_${d##*_}.txt" writes the directory name to a txt file in that directory.

With an one-liner:

for d in dir_*; do printf "%s\n" "$d" > "$d/file_${d##*_}.txt"; done

I have favored printf to echo because there are special cases that are handled correctly by the former but not by the latter, although in the particular case presented echo would not arise problems. Find more in Why is printf better than echo?.


If you need to avoid non-directories that also begin with dir_, option 1 is to add the [ -d "$d" ] && test right before printf statement in the script above. Option 2 is to use dir_*/ so that only directories are matched:

for d in dir_*/; do
    dirn=${d%%/}
    printf "%s\n" "$dirn" > "${d}file_${dirn##*_}.txt"
done
Quasímodo
  • 18,865
  • 4
  • 36
  • 73
0
find . -name 'dir_[0-9]*'  -type d -execdir bash -c 'name=$0;printf "${name##*/}\n" > "$name/file_${name##*_}.txt"' '{}' \;

Execute multiple commands from find result using -execdir

name=$0; => store each directory name in $name variable and process it.

  • Sorry for being a pest here, but: 1) * isn't the only special character in dir_[0-9]\*, the full pattern should be quoted ('dir_[0-9]*'): as it is, it breaks if the current directory contains a directory named e.g. dir_1* (with a literal *). 2) Embedding {} in shell code is an injection vulnerability - it'd be better to use bash -c 'name="$1"; ...' sh '{}' \; (cont...) – fra-san May 22 '20 at 15:20
  • (...cont) 3) please refer to my comment to the other answer about why printf should be preferred to echo. 4) Expansions should always be quoted, unless you really need them not to be: think about people who modifies your script to make it also find files whose name may contain spaces. – fra-san May 22 '20 at 15:21
  • no problem , explanation about small things helps a lot ...your points are correct , also i would add that instead of bash -c 'name="$1"; ...' sh '{}' \; we can use bash -c 'name="$1"; ...' '{}' \; just without sh ....will edit my answer... – Stalin Vignesh Kumar May 22 '20 at 15:27
  • The first argument after bash -c 'name="$1"; ...' is used as the name for the script and is assigned to $0, you need some string there, before '{}'. Otherwise $1 just remains unset (as you have seen). I'd also use "$name/file_${name##*_}.txt" (in double quotes). And give '%s\n' as a format string to printf. – fra-san May 22 '20 at 15:36
  • yes , positional arguments starts with $0 , so i preferred that...thanks... – Stalin Vignesh Kumar May 22 '20 at 15:45
  • What I meant is: find . -name 'dir_[0-9]*' -type d -execdir bash -c 'name=$1; printf "%s\n" "${name##*/}" > "$name/file_${name##*_}.txt"' mybash '{}' \;. Please have a look at it - I'm not further bothering you, thank you for your patience. – fra-san May 22 '20 at 15:59