4

I am trying to store the name of list of files that contain 781 in their name. I will be using this list to perform some operations and after that I will delete this list.

I have two options to do this.

Option 1 : I can store the list in a temporary file using below command.

ls -lart *781* | awk '{print $9}' > tempo

File tempo will contain the list and I will delete this file later after using.

Option 2 : If I can somehow store the list in a variable and use that variable instead. I am trying below code to implement this but this doesn't work.

filelist=`ls -lart *781* | awk '{print $9}'`
echo $filelist

But above code doesn't give any output. Please help me out with the syntax.

Edit: I am using k-shell.

2
  • What shell do you use?
    – Mat
    Dec 19, 2012 at 12:02
  • I am using k-shell
    – g4ur4v
    Dec 19, 2012 at 12:21

4 Answers 4

3

Do not use ls to get the file list, especially ls * that will give you lots of blank lines for directories.

You could use (which is not always good):

for file in *781*; do
  echo "$file"
done

Which will survive files with spaces, but not files with other special characters.

You can also do:

 find . -name "*781*" -print0 | while IFS="" read -r -d "" file; do
   echo "$file"
 done

Which is much better, but not standard in all UNIXes (Linux ok).

In all cases, do not put filenames in a single variable, as you cannot use space as a delimiter. Maybe you could use bash arrays as bellow:

declare -a file_array
while IFS="" read -r -d "" file; do
  file_array[${#file_array[@]}]="$file"
done < <(find . -print0)

To work in ksh where you do not have process substitution:

typeset -a file_array
mkfifo mypipe
find . -print0 > mypipe &
while IFS="" read -r -d "" file; do
  file_array[${#file_array[@]}]="$file"
done < mypipe
rm mypipe

For more information, you can check this and this.

3
  • 1
    +1 Good answer. ksh88 and later do have process substitutions. mksh (and by extension, pdksh and derivatives) do not. ksh93 was not able to read a null-delimited list until just a month ago or so in the very latest alphas, while mksh and most other ksh-like shells with a read -d feature have been able to for a long time. So what works heavily depends on what ksh you're using. Finally, no ksh has declare. You should use typeset when working with anything but Bash.
    – ormaaj
    Dec 19, 2012 at 13:05
  • 1
    "... especially ls -r *": you mean ls -R *.
    – dubiousjim
    Dec 19, 2012 at 13:12
  • @dubiousjim: simply ls * gives an output which is not easy to parse if one of the file is a directory. Edit done.
    – jfg956
    Dec 19, 2012 at 13:15
0

SUGGESTION, inspired on the idea of an anonymous pipe, and the need to use the output of a function as the input to an array, I found a co-process can help.

MyFunction()
{ echo "One line" ; echo "Second line" ; echo "Third line" }

set -A array
MyFunction 'SomeInputToFunction' < /dev/null |&

while read -p -r line; do
    print "READING from co-process: $line"
    array[ ${#array[@]} ]="$line"
done

Output:

READING from MyFunction co-process: One line
READING from MyFunction co-process: Second line
READING from MyFunction co-process: Third line

Thanks a lot to the previous posters! -Fernando Gonzalez

-1

Not sure which shell you are using but with bash

export filelist=`ls -lart *781* | awk '{print $9}'`
echo $filelist

as example:

export filelist=`ls -l|awk '{print $4}'`
echo $filelist
root root sys nobody sys sys sys sys root sys bin root sys root other sys sys root root sys other root sys root sys sys root root
-1

I don't know why your examples aren't working. They work here---if I use a glob expression that matches any files. Note that if any of the matches might be a directory, and you don't want to list the contents of that directory, you should add a -d flag to your ls.

You have to watch out for what happens when your glob expression doesn't match any files. Bash has a setting you can enable with shopt -s failglob that gives an error in that case. In other shells, or when this option hasn't been enabled in bash, you'll instead get the glob expression itself as output. If you just do a echo nonmatching_expression* you'll see the result "nonmatching_expression*". If you do ls nonmatching_expression*, ls should throw an error.

As written, both of your options assume that the filenames contain no whitespace. This is because of the ls -l ... | awk '{print $9}'. If you know your filenames don't contain any whitespace, that's fine. (But why are you using ls -l ... | awk '{print $9}' instead of just ls? I assume you have some reason not manifest in the above examples.)

If your filenames might contain newlines, things will be substantially harder---I won't go into what to do in that case. If they won't contain newlines but might contain other whitespace, then you could omit the -l flag on ls and omit the pipe to awk. Then your first option would save into the temporary file one filename per line. Those filenames could contain spaces or tabs (but not newlines). Your second option would also store one filename per line into the variable, however these will be harder to work with.

If you've saved the filename list to a variable, and filenames might contain some whitespace, then you should be sure to always quote the variable when expanding it: so use echo "$filelist" not echo $filelist.

Here's how I would extract the data:

Option 1:

while IFS= read -r f; do
  do_stuff_with "$f"
done < tempo

Option 2:

while IFS= read -r f; do
  do_stuff_with "$f"
done << EOF
$filelist
EOF

These methods permit you to perform operations inside the loop (modifying variables, changing directories) that will be visible to the rest of the script. If you don't care about that, then you can instead have the while ... done section in a pipeline, for example like this:

echo "$filelist" | while IFS= read -r f; do
  do_stuff_with "$f"
done

In that case, the whole while ... done construct is executed in a subshell, and any modifications to variables (including the looping over f) will only be visible inside that subshell.

Bash and some other shells have array variables. Some people find these easier to work with when the filenames might contain whitespace. However, they are less portable. And they cannot be exported. (You tagged your question as /environment-variables, which to me implies that the variables will be exported. But perhaps you didn't mean it that way.) I won't go into how you might achieve your ends with array variables.

If you know you don't have any whitespace in your filenames, then it's simplest to use your option 2, and you can use this method:

for f in $filenames; do
    do_stuff_with $f
done

Here quotes must be omitted around $filenames and are optional around $f.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .