5

I have a list of files in one directory and a set of jpegs corresponding to each file in another directory. I need to loop over all files, and for each file name, determine the target directory.

For example, if I have three text files called foo.txt, bar.txt and baz.txt in /home/userA/folder/, the corresponding jpegs will be in /home/userA/folder2/foo/, /home/userA/folder2/bar/ and /home/userA/folder2/baz/.

I wrote a script that should loop over all txt files and get the corresponding target directories but it's giving me an error:

bash: /home/userA/folder/File1.txt: syntax error: operand expected (error token is "/home/userA/folder/File1.txt")`

My script:

#!/bin/bash
FILES=/home/userA/folder/*.txt
for i in $FILES
do
    str1=$i | cut -d'/' -f5 #to get the name of the file
    echo /home/userA/folder2/$i_filename #I want to include the .txt filename in the output to be like this /home/userA/folder2/Text1_filename    
done

How can I fix this?

Tak
  • 519
  • 4
  • 11
  • 23

6 Answers6

3

If all you want is to get the file's name and use it to get the right target directory, you can do:

#!/bin/bash
for i in /home/userA/folder/*.txt
do
    ## Get the file name
    str1="${i##*/}"

    ## Get the target directory
    dir="/home/userA/folder2/${str1%.txt}/"
done

This is using the shell's native string manipulation features. ${var##pattern} removes the longest match of pattern from the start of $var and ${var%pattern} removes the shortest match of pattern from the end of $var. So, ${i##*/} removes everything until the last / (the path) from the file name and ${i%.txt} removes the string .txt from the end of it.

terdon
  • 234,489
  • 66
  • 447
  • 667
2

You just forget the echo and backquotes and field number on this line

    str1=`echo $i | cut -d'/' -f5 `#to get the name of the file

But basename might be a better option.

    str1=`basename $i` #name of the file

Like this

#!/bin/bash
FILES=/home/userA/folder/*.txt
for i in $FILES
do
    str1=`basename "$i"` #to get the name of the file
    echo $str1
    ls -l "`dirname "$i"`/$str1"
done

For a good answer about dealing with for loops and files with spaces in their name refer to This answer

X Tian
  • 10,413
  • 2
  • 33
  • 48
  • Thanks for your answer but this didn't fix the problem. The error still appears – Tak Jun 24 '15 at 11:31
  • yes have updated. – X Tian Jun 24 '15 at 11:33
  • I still get the same error. Actually the script doesn't enter the loop. It stops giving this error in the `FILES=/home/userA/folder/*.txt` where `File1` (which is the first .txt file in the location) is fetched. – Tak Jun 24 '15 at 11:37
  • your answer still gives the same error. Seems there is something wrong with the `FILES=/home/userA/folder/*.txt` – Tak Jun 24 '15 at 12:06
  • look at/show the output of `ls -1 /home/userA/folder/*.txt` do some of your files have spaces in them ? Check the link I added at the end of my answer. – X Tian Jun 24 '15 at 12:16
  • I checked it and no there are no spaces at all. – Tak Jun 24 '15 at 12:19
  • show us the output of `bash -x -c FILES=/home/userA/folder/*.txt;echo $FILES` – X Tian Jun 24 '15 at 12:35
  • `/home/userA/folder/File1.txt /home/userA/folder/File2.txt /home/userA/folder/File3.txt` and so on – Tak Jun 24 '15 at 12:39
  • @shepherd Quote `"/home/userA/folder/*.txt"` – 123 Jun 24 '15 at 12:45
  • @User112638726 this still gives the same error – Tak Jun 24 '15 at 12:49
2

Using find:

#!/bin/bash
path="/home/userA/folder"
find "$path" -maxdepth 1 -type f -name "*.txt" -print0 | while read -d $'\0' file; do
    a="$path/$(basename $file)/a_%06.jpg"
    echo "$a
done
A.B.
  • 3,372
  • 2
  • 17
  • 27
2

Do you mean this?

 ~/test $ for i in F/*.txt; do n="TARGET/$(basename "$i" .txt)"; echo "$n"; done
TARGET/a
TARGET/b
TARGET/c
 ~/test $ ls F
a.txt  b.txt  c.txt
 ~/test $

And with special chars and spaces:

 ~/test $ for i in F/*.txt; do n="TARGET/$(basename "$i" .txt)"; echo "\"$n\""; done
"TARGET/a b€C\""
"TARGET/a"
"TARGET/b"
"TARGET/c"
 ~/test $ ls F
a b€C\".txt  a.txt  b.txt  c.txt
ikrabbe
  • 2,155
  • 12
  • 20
  • Yup. But this is the same as [Xtian's answer](http://unix.stackexchange.com/a/211810/22222). – terdon Jun 24 '15 at 14:25
  • The difference is, that basename filters the extension by the second argument `basename "$i" .txt` so the .txt isn't returned. – ikrabbe Jun 24 '15 at 14:29
  • Oh, true, I missed that. In his defense, that wasn't in the original version of the question. – terdon Jun 24 '15 at 14:33
0
find /home/userA/folder/ -maxdepth 1 -type f -name "*.txt" -print0 | while read -r -d '' file; do filename=$(printf "%s\n" "$file" | awk -F'/' '{gsub(".txt","");print $5}' ); printf "/home/userA/folder2/%s" "$filename"; done
Sergiy Kolodyazhnyy
  • 16,187
  • 11
  • 53
  • 104
0

If you really want to put it in a variable, you can use a bash array:

#!/bin/bash
FILES=(/home/userA/folder/*.txt)
for i in "${FILES[@]}" # double qouting pervents extra word splitting
do
    bn="$(basename "$i")" # to get the name of the file
    a="/home/userA/folder2/$bn/a_%06d.jpg"
done

Or you can simply use for i in /home/userA/folder/*.txt.

Mingye Wang
  • 1,181
  • 9
  • 23
  • small question though, if there's space in filenames, isn't that array element going to be split into two ? – Sergiy Kolodyazhnyy Jun 24 '15 at 13:31
  • @Serg Luckily, no. Try this snippet: `mkdir -p /tmp/t; cd /tmp/t; touch 1 '2 3' ' 4'; printf '%q ' *; echo $'\tdirect * args'; arr=(*); printf '%q ' "${arr[@]}"; echo $'\tafter array expansion'`. – Mingye Wang Jun 24 '15 at 13:33
  • @Serg As some extra information, *File/Pathname Expansion* is performed after *Field Splitting* in POSIX [Shell Command Language](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.htm). GNU Bash does [the same](http://www.gnu.org/software/bash/manual/html_node/Shell-Expansions.html), although they call these two *Word Splitting* and *Filename Expansion* respectively. – Mingye Wang Jun 24 '15 at 13:37
  • printf: %q: invalid conversion specification direct * args printf: %q: invalid conversion specification after array expansion – Sergiy Kolodyazhnyy Jun 24 '15 at 13:39
  • @Serg We are talking about bash, so I assumed that you tested it in bash where there is `%q` in the `printf` builtin which can help you correctly quote the arguments. Since we aren't getting that serious, use `f(){ for i; do echo -n "'$i' "; done; }; mkdir -p /tmp/t; cd /tmp/t; touch 1 '2 3' ' 4'; f *; echo $'\tdirect * args'; arr=(*); f "${arr[@]}"; echo $'\tafter array expansion'`. – Mingye Wang Jun 24 '15 at 13:42
  • @Serg but wait a moment.. How can you go with bash arrays without bash? Are you using some distros like Debian where some builtins have been cut off from `bash` so you automatically falls to the `env printf` one? – Mingye Wang Jun 24 '15 at 13:43
  • I've got mksh, actually. With bash it worked, though – Sergiy Kolodyazhnyy Jun 24 '15 at 13:45