6

Is there some command to search a directory for files and output the result to a file with a prefix text before name of file that was found?

For example, I have a directory with these files:

sound1
sound2
sound3 
…

Then give a command to search the directory and write the results to a text file with a prefix before it, e.g.

media sound1
media sound2
media sound3
…

Bonus question

Is there a way to write "media1", "media 2" etc... instead of just "media" to an existing text file without deleting what was inside that text file before?

media1 sound1
media2 sound2
media3 sound3
…
dessert
  • 39,982
joblade
  • 61
  • 1
  • 5

5 Answers5

4

How about a simple for loop?

for i in *; do echo media $i >> text_file; done

Explanations

  • * – expands to every file in the current directory
  • echo media $i – prints “media ” and the currently processed filename
  • >> text_file – redirects the output of echo to a file called text_file appending it

Example

$ ls
sound1  sound2  sound3

$ for i in *; do echo media $i >> text_file; done

$ cat text_file 
media sound1
media sound2
media sound3

Bonus question

k=0; for i in *; do ((k++)); echo media$k $i >> text_file; done

Or for very many files (slightly faster):

j=text_file; k=0; for i in *; do if [[ "$i" != "$j" ]]; then ((k++)); echo media$k $i; fi; done > "$j"

Explanations

  • k=0 – define variable k and set it to 0
  • ((k++)) – increment $k by one

Example

$ ls
sound1  sound2  sound3

$ k=0; for i in *; do ((k++)); echo media$k $i >> text_file; done

$ cat text_file 
media1 sound1
media2 sound2
media3 sound3
dessert
  • 39,982
  • 1
    Nice and simple :) You can change it in this way: for i in *; do echo media${i} $i >> text_file; done to meet this requirement. – pa4080 Sep 29 '17 at 11:47
  • 1
    ls -1v | awk '{printf "media%d %s\n", NR, $0}' > text_file works perfectly if you make it ls -1v | awk '{printf "media%d %s\n", NR, $0}' >> text_file (add a ">" symbol before file name – joblade Sep 29 '17 at 12:44
  • @joblade It doesn't work for me, but includes text_file in the output – I added my alternative solution. – dessert Sep 29 '17 at 12:55
  • Why do you open&close the file on each iteration? You could instead simply do for .... done > text_file. – Ruslan Sep 29 '17 at 14:40
  • @Ruslan Did you try this solution? You can't do that because * will then also list text_file. You could exclude it with [[ "$i" != text_file ]] && … though. – dessert Sep 29 '17 at 16:36
  • @Ruslan Just tested with 100 files and time: The echo >> text_file way needs 0.011s, the done > text_fileway with if statement needs 0.007s. I don't consider this a great enough difference in everyday use. Still I'll edit it in – thanks for the remark! – dessert Sep 29 '17 at 16:51
4

This aught to help:

find /path/to/sound/files -type f -name "sound[0-9]" -printf 'media %f\n' > file.txt

Information:

sound[0-9]: Look for file with names that have sound and ends with a number

-printf 'media %f\n': Format the filesname with a prefix of media.

> file.txt: Send it to file called file.txt.

George Udosen
  • 36,677
3

You can use find command in this way:

find . ! -name 'sound.txt' -type f -exec sh -c 'echo "media $(basename {}) "' \; > sound.txt

Explanation:

  • The first argument is the path to the directory, in the example this is the current directory ., but you can change it to /any/desired/path/.

  • ! -name 'sound.txt' - means that items with name sound.txt will be omitted.

  • -type f - means only files will be finding.

  • -exec <command> \; - instruct the find command to execute <command>.

  • {} is the output argument from the entire command before -exec - find . ! -name 'sound.txt' -type f.

  • sh -c 'echo "media $(basename {}) "' is the <command> that will be executed:

    • sh -c - execute a command within /bin/sh shell;
    • 'echo "media $(basename {}) "' - command to be executed:
    • echo "<something>" - output <something> to STDOUT;
    • media - just a string;
    • $(<some command>) - treat the result of <some command> as string;
    • basename {} - print only the filename without its path, where {} is the variable that contains the full filename generated by find . ! -name 'sound.txt' -type f
  • Finally > redirects STDOUT;

  • sound.txt is the name of the file where STDOUT will be redirected.

Update from the comments. Within the comments there are two suggestions for simplification of the above command:

  • The initial command:

    find . ! -name 'sound.txt' -type f -exec sh -c 'echo "media $(basename {}) "' \;
    
  • Dessert's suggestion:

    find . ! -name 'sound.txt' -type f -exec sh -c 'echo media ${0##*/}' {} \;
    
  • Steeldriver's suggestion:

    find . ! -name 'sound.txt' -type f -printf 'media %f\n'
    

Example of usage. Let's assume we have a directory with the following structure:

$ tree .
.
├── sound
├── sound1
├── sound2
├── sound3
├── sound4
├── sound5
├── sound99
└── soundd

The outputs of each of the above command will be identical each other:

$ find . ! -name 'sound.txt' -type f -printf 'media %f\n'
media sound1
media soundd
media sound3
media sound5
media sound2
media sound
media sound99
media sound4

Bonus question. According to the bonus question, the find command isn't the best choice, but its output could be piped (|) to another command or cycle. And here are two cases:

  • The first case is when we want to just count the media# results:

    find . ! -name 'sound.txt' -type f -printf 'media %f\n' | sort | \
    while read -r i; do \
        count=$((count+1)); \
        echo $i | sed "s/media/media$count/"; \
    done
    

    The example output of this command will be:

    $ find . ! -name 'sound.txt' -type f -printf 'media %f\n' | sort | while read -r i; do count=$((count+1)); echo $i | sed "s/media/media$count/"; done
    media1 sound
    media2 sound1
    media3 sound2
    media4 sound3
    media5 sound4
    media6 sound5
    media7 sound99
    media8 soundd
    
  • The other case is when we want to assign the number of each sound# file to its media prefix. And because into the example's circumstances has two files without any number within their names (sound, soundd), we shall add unique number to them, so the prefix will be media/#:

    find . ! -name 'sound.txt' -type f -printf 'media %f\n' | sort | \
    while read -r i; do \
        number=$(echo $i | grep -Eo '[0-9]+'); \
        if [ -z $number ]; then \
            count=$((count+1));
            number="\/$count"; \
        fi; \
        echo $i | sed "s/media/media$number\t/"; \
    done
    

    Or we can use this more simple syntax:

    find . ! -name 'sound.txt' -type f -printf 'media %f\n' | sort  | \
    while read -r i; do \
        number=$(echo $i | grep -Eo '[0-9]+'); \
        [ -z $number ] && count=$((count+1)) && number="\/$count"; \
        echo $i | sed "s/media/media$number\t/"; 
    done
    

    The example output of this command will be:

    $ find . ! -name 'sound.txt' -type f -printf 'media %f\n' | sort | while read -r i; do number=$(echo $i | grep -Eo '[0-9]{1,99}'); [ -z $number ] && count=$((count+1)) && number="\/$count"; echo $i | sed "s/media/media$number\t/"; done
    media/1  sound
    media1   sound1
    media2   sound2
    media3   sound3
    media4   sound4
    media5   sound5
    media99  sound99
    media/2  soundd
    
  • Hints:

    • You can copy and paste a multi line command directly to the terminal.

    • Don't forget > sound.text into the end of the command to push the output into a file.

    • Wired topic.

pa4080
  • 29,831
  • @dessert: I think this part ! -name 'sound.txt' solve this issue. Isn't it? – pa4080 Sep 29 '17 at 11:42
  • 1
    You can spare basename with -exec sh -c 'echo media ${0##*/}' {} \;. – dessert Sep 29 '17 at 11:46
  • @dessert, thanks for this advice. I'm trying to figure out how can I add a counter for media # within find command, any ideas? – pa4080 Sep 29 '17 at 11:56
  • 2
    You can replace -exec sh -c 'echo "media $(basename {}) "' {} \; with -printf 'media %f\n' – steeldriver Sep 29 '17 at 12:34
  • Thank you, @steeldriver, I've tried something similar, but without success... I will elaborate the answer. – pa4080 Sep 29 '17 at 12:41
  • @pa4080 I'd just pipe it through this: sed = | sed 'N;s/\n/ /;s/^/media/', like find . ! -name text_file -type f -printf "%f\n" | sed = | sed 'N;s/\n/ /;s/^/media/' – dessert Sep 29 '17 at 13:07
2

First print the list of files (one entry per line) in a file (list_of_files)

ls -1 /location/of/the/directory > list_of_files

Then add a fixed prefix (media) to each line

sed -i 's/^/media /' list_of_files

Update for bonus question

To add numbering, first print the list of files in a file (say list_of_files) as before. Then add line numbers before each line

sed -i '=' list_of_files

The output would look like this

1
sound1
2
sound2
...

Then fix the line-breaks

sed -i '{N;s/\n/ /}' list_of_files

The output would now look like this

1 sound1
2 sound2
...

Then finally add a fixed prefix (media) to each line like before

sed -i 's/^/media/' list_of_files

Final output would be

media1 sound1
media2 sound2
...
pomsky
  • 68,507
  • @dessert ls lists one entry per line for me. But better be safe, I changed it as per your suggestion. Thanks. – pomsky Sep 29 '17 at 11:16
  • @dessert Clarification: What I meant was ls > filename prints one entry per line in filename, but ls in terminal lists names in a single line. I think we were having a little communication gap. – pomsky Sep 29 '17 at 11:50
  • @dessert it's a feature of many utilities to pretty-print to a tty, but output in a compatible mode when redirected to file. E.g. ls --color=auto, grep --color, modern gcc (≥5.0) by default print colored output to a terminal, but resort to plain text if stdout is not a tty. Similarly, ls by default prints multicolumn to a tty, but single-column to not-a-tty. See isatty(3). – Ruslan Sep 29 '17 at 14:45
0

Try this script:

#!/bin/bash
dir=`find /home/username/ -name script_test`
ls $dir  > test.txt
sed -i -e 's/^/media /' test.txt
Eliah Kagan
  • 117,780
Beginner
  • 491
  • 1
  • 9
  • 28
  • Cool stuff. It's working. Is there a way to write "media1", "media 2" etc... instead of just "media" to an existing text file without deleting what was inside that text file before? – joblade Sep 29 '17 at 10:34
  • thats very easy i will update. now I'm travelling once reached i will updATe it. – Beginner Sep 29 '17 at 11:40
  • 1
    Even though this solution does work, I'd recommend editing your answer to include an explanation of the commands. That would make it more informative and a better answer overall. – TheOdd Sep 29 '17 at 13:24
  • if you want points take it – Beginner Oct 03 '17 at 07:48