My script doesn't work in the terminal window!

I am inexperienced with scripting and it continues to baffles me speechless

I wrote a script so that it counts the number of subset of files (with different prefix) in all subdirectories under the current directory so that

find ./ -type d -maxdepth 1 -mindepth 1 > list_of_dir.txt

find all subdirectories under the current directory and output the list to txt file

sed 's/^...//' <list_of_dir.txt >list_of_dir2.txt

edit the output file so that .//directory becomes directory

rm list_of_dir.txt 
echo list of data > list_of_data.txt
for i in {1..$(wc -l < list_of_dir2.txt)}
do
echo "\n$(sed -n "$i"p list_of_dir2.txt)" >> list_of_data.txt
cd ./"$(sed -n "$i"p list_of_dir2.txt)"
ls *.jpeg | awk -F_ '{A[$1 FS $2]++} END {for (j in A) printf "%-20s%4d \n", j, A[j] >> "../list_of_data.txt"}' 
cd ..
done

from each subdirectory in the txt file, output the directory name to file list_of_data, then cd into that directory, then count all the number of subset of files under different prefixes and output the result to list_of_data.txt

The problem is, if I run the script by typing ./scriptname.sh I get the error message

sed: 1: "{1..23}p": invalid command code .
sed: 1: "{1..23}p": invalid command code .
ls: cannot access *.jpeg: No such file or directory

but if I copy the content of the script and paste it in the terminal window it worked.

sample output

directoryname
directoryname_1 10
directoryname_2 5

directoryname2
directoryname2_1 20
directoryname2_2 30

etc etc

Why is this happening?

How does your script start?

the first line of code is how the script start

{1..$(wc -l < list_of_dir2.txt)}

This does not work. You cannot put a command there.

Your other commands look like they are using sed and the like to extract a single line per loop. This is extremely poor practice.

Please explain what you are trying to do here.

Thanks for your reply. As I said I am inexperienced I am sure there are much better way to write this.

Can you explain why the code works if I paste it in the terminal but it doesn't if I run it as a script?

As I explained above there are many subdirectories under the current directory. And inside each subdirectory there are a large number of files under different prefixes, like pict_1_1, pict_1_2, pict_2_1, pict_2_2, pict_2_3 etc etc etc

I want to count the number of file under each prefix from every subdirectory. with output like the sample output above into a txt file.

I would also appreciate it if you explain why you meant the extreme poor practice. I am keen to learn.

If a script lacks a #! first line it is sent to sh not bash. See man execvp. Man Page for execvp (opensolaris Section 0) - The UNIX and Linux Forums

I think I am beginning to understand what the problem is. It seems the problem occurs with the sed command in the for loop. the error message

sed: 1: "{1..23}p": invalid command code .

stems from the fact that there are 23 folder in current directory and maybe I don't truely understand the mechanism of the for loop.....as

for i in {1..23}

then the

sed -n "$i"p list_of_dir2.txt

command tries to execute sed {1..23} all at the same time, hence the error.
During the for loop is it not like for i =1 then carries out the commands, then for i=2, carries out the command again etc etc until i=23? Why does sed trying to interpret i=1..23 all at the same time?

Still don't understand why the command runs when typed in terminal window but didn't when ran as script.

Well that is why I asked how your script starts: you seem to be using bash... the syntax you use will not be understood by bourne shell (default unless you specify as first line of your script...)
try to add as firstline of your script:

#!/bin/bash 

thanks vbe for your reply.

I am using zsh...

echo $shell

/sw64/bin/zsh

so add

#!/sw64/bin/zsh

at the top of your script and test...

nope...same thing happened :frowning:

sed: 1: "{1..       23}p": invalid command code .

but when I pasted the commands in the terminal it worked.

Can you put here the script to the line after the for loop with the sed line, is your script executable?

Try to run your script in debug mode. After 1st line of the script right this.

set -x

Also check this link. May be it can help you out.

bash - commands work in linux terminal but not in shell script - Stack Overflow

{1..23} is bash, but without the #! line it runs as sh script and becomes a literal string.

Here is the script

#!/sw64/bin/zsh

find ./ -type d -maxdepth 1 -mindepth 1 > list_of_dir.txt

sed 's/^...//' <list_of_dir.txt >list_of_dir2.txt

rm list_of_dir.txt 

echo list of data > list_of_data.txt

no_of_dir=$(wc -l < list_of_dir2.txt)

for i in {1..$no_of_dir}

do

current_dir=$(sed -n "$i"p list_of_dir2.txt)

echo "\n"$current_dir >> list_of_data.txt

cd $current_dir

ls *.jpeg | awk -F_ '{A[$1 FS $2]++} END {for (j in A) printf "%-20s%4d \n", j, A[j] >> "../list_of_data.txt"}' 

cd ..

done

yes the script is executable

OK, assuming that is the right zsh path, and if not there would be an error, is '{1..$no_of_dir}' a correct expanding string? Well, I have no zsh but the zsh doc says it is OK if the variable is expanded first. If not, you need an eval or something. Your use of it seems abuse (lose things in pile and then grope for them), why not just

while read current_dir;do ... done <  list_of_dir2.txt

I wonder if its not something to do with your environment... You have something there that is missing in your scripts like PATH to a different find or sed etc... because -maxdepth is not standard in solaris so I can guess you are using gnu find, and so may also have another sed than the standard...
Have a check...

Hi DGPickett

I have just come across the while read (file) option and it works beautifully!

Thanks everyone for your help. although I still dont' quite understand why the for loop and sed combination didn't work.

Yes, "while read" is a keeper, pipeline parallel and robust. Read can capture fields, and bash has "read -a simple_array_var_typedef_-a" so you do not even have to count or name them. I mostly use "while read a b c" where c gets the rest. I nevr use 'while read' with no vars, as I have to shift and type more for $REPLY.

One recent post noted that "for i in {1..999999999}" might make a command line too long, slowly, but "i=0; while (( ++i < 999999999 )) ; do ... done" has no such problems.

1 Like

Thanks DGpickett but I have no idea what you talking about lol