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
  • 529

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
  • 242,166
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,463
  • 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,442
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,163
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
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
  • 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. GNU Bash does the same, 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