Solved Using tee with multiple output files.

Phishfry

Son of Beastie

Reaction score: 1,396
Messages: 4,113

I am trying to write a script that appends a line of text to a group of files.
Currently I have a text list of directories with complete path, but no file name.
Here is my sample I am building from:
echo "hello" | tee -a file1 file2
It works fine but I need to output to 130 Makefiles in different directories.
So I have a list of ports and I need to use it as part of the output pipe.
Plus need to suffix a filename(Makefile) on the end of these directories.
My ports list looks like this[snipped]:
depends.list
Code:
/usr/ports/graphics/tiff
/usr/ports/graphics/jbigkit
/usr/ports/graphics/jpeg-turbo
/usr/ports/devel/nasm
/usr/ports/graphics/png
/usr/ports/graphics/jasper
So what I have done is used a variable in a script to define files to use for output.
tee.sh
Code:
#!/bin/sh
portlist=/usr/ports/graphics/xv/depends.list
echo "test" |tee -a ${portlist}/Makefile
Problem with that is it is interpreting the list wrong. I need it to process the contents of that file.
Code:
:~ # sh tee.sh
tee: /usr/ports/graphics/xv/depends.list/Makefile: Not a directory
test
How can I have it read the contents of this file and not try and use it literally. Is this what readline does?

Thanks for your help.
 
OP
OP
Phishfry

Phishfry

Son of Beastie

Reaction score: 1,396
Messages: 4,113

Same failure with that. My variable does not seem to be operating the way I hoped
test.sh
Code:
#!/bin/sh
PORTLIST=/usr/ports/graphics/xv/depends.list
for i in ${PORTLIST}/Makefile
do
echo "test" >> $i
done
Failing on the path:
Code:
/usr/ports/graphics/xv # ./test.sh
./test.sh: cannot create /usr/ports/graphics/xv/depends.list/Makefile: Not a directory
When I point my variable to an actual directory inside my list it works fine. (Although the text ends up at EOF)
 

aragats

Daemon

Reaction score: 515
Messages: 1,246

Well, I guess find should work straight forward:
Code:
find $PORTLIST -name "Makefile" -exec echo "test" >> {} \;
 

ralphbsz

Daemon

Reaction score: 1,161
Messages: 1,871

Code:
portlistfilename=.../depends.list
portlist=`cat ${portlistfilename}`
for directory in ${portlist}
do
    echo "test" >> ${directory}/Makefile
done
Can you see what I did here? First, the variable portlistfilename is a variable containing a single string, the file name of the port list. Second, the variable portlist is really a list, containing all the ports (not a single string with the name of the file containing the list). And then, I didn't try to implicitly append /Makefile to each, but instead I just iterate over the items in the list, and append later.

This code is simple enough, it should work even in old-fashioned /bin/sh. In modern shells (like bash) you can use tricks to turn variables into arrays, and then use arrays in expansion, but I prefer the verbose, simple, and clear version.
 
OP
OP
Phishfry

Phishfry

Son of Beastie

Reaction score: 1,396
Messages: 4,113

I though about this incarnation earlier. Borrowed your variable name.
portlist='printf ${portlistfilename}'
As a way to 'load' the list. I see you used cat instead. I like the two variable appoach ralphbsz shows here.
I couldn't have come up with it on my own.
I did get so frustrated that I just modified my depends.list directly into a script.
Will try this again as this is just for learning.
 
OP
OP
Phishfry

Phishfry

Son of Beastie

Reaction score: 1,396
Messages: 4,113

I really tried hard with sed but escape characters are really hard to figure out.
This was my best effort after a day of messing with web examples:
sed -i '.orig' '1s;^;prepended text;' test.txt
It does not do a linefeed/CR though. I used inplace mode and wanted to save a .orig copy.
 

leebrown66

Well-Known Member

Reaction score: 134
Messages: 400

If you are trying to read the contents of a file and process each line, then use do with redirection.

test.sh
Code:
#!/bin/sh
PORTLIST=/tmp/test.list
while read op
do
echo "op is ${op}"
done < ${PORTLIST}
test.list
Code:
file1
file2
file3
Code:
sh ./test.sh
op is file1
op is file2
op is file3
 
OP
OP
Phishfry

Phishfry

Son of Beastie

Reaction score: 1,396
Messages: 4,113

I worked on ralphbsz first and it worked with no problems. Exactly as I wanted.
Code:
#!/bin/sh
LISTFILENAME=/usr/ports/graphics/xv/depends.list
PORTLIST=`cat ${LISTFILENAME}`
for DIRECTORY in ${PORTLIST}
do
        echo "test" >> ${DIRECTORY}/Makefile
done
I really appreciate the plaintext for the DIRECTORY. It makes it much more readable than ${i}
 

ralphbsz

Daemon

Reaction score: 1,161
Messages: 1,871

That's actually a very deep and philosophical question in software engineering. Here are two code samples, in a hypothetical C-like language. They are both calculating something for an image (just the sum of the pixels), and I'm treating the image as an array. Let's assume that the width and height of the image

Concise version:
Code:
t = 0
for (int i=0; i<w; ++i) {
    for (int j=0; j<h; ++j) {
        t += image[i][j];
    }
}
Verbose version:
Code:
total_pixels = 0
for (int index_x=0; index_x<image_x_width; ++index_x) {
    for (int index_y=0; index_y<image_y_width; ++index_y) {
        total_pixels += image[index_x][index_y];
    }
}
Which of the two is more readable? Good question. I personally prefer concise variables in cases where the variable stays in scope only for a line or two, and verbose ones for variables that last a long time. But in reality, a lot of the answer depends on your expectations of who is going to read the code. If it is an image-processing expert, you can rely on them knowing what i, j, w, h, x and y mean. If it is a beginner, then "image_x_width" is a good reminder. And also remember: Anyone can be an fool part of the time, either because they haven't learned yet, or because they have forgotten. So make your code more fool-proof, unless you want it to be easy for experts and you can rely on no fools being in the area.
 
Top