Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

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.

share|improve this question
    
What shell do you use? –  Mat Dec 19 '12 at 12:02
    
I am using k-shell –  g4ur4v Dec 19 '12 at 12:21

4 Answers 4

up vote 3 down vote accepted

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.

share|improve this answer
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 '12 at 13:05
    
Thanks ormaaj for the typeset comment. Edit done. –  jfgagne Dec 19 '12 at 13:11
1  
"... especially ls -r *": you mean ls -R *. –  dubiousjim Dec 19 '12 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. –  jfgagne Dec 19 '12 at 13:15

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

share|improve this answer

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
share|improve this answer

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.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.