usage of echo with standard input

Hi,

I am passing the file filename to a program and I have to use the "<" sign:
program < filename

Inside the program I would like to assign the string "filename" to a variable var.
I can't do that with "var=$(echo $1)" because of the "<" sign.
Removing the "<" sign would work for echo, but not for the rest of the program.

Can someone help me sorting this out, using echo or whatever command is suitable?

Cheers

What is wrong with:

var="$1"

thanks for your reply
Using var="$1" I get an empty line when trying to display the variable (echo $var)
It works if I run the program with:
program filename
but as I said I have to use a "<" sign

if "$1" has some data in it - assume it is not unset - then var="$1" places data in to the variable "var". Okay?

You seem to want to be able to echo to the screen, not use echo to write to a variable from a child process - which is what I first gave you as a workaround.

echo "$1" > /dev/tty
# or
echo "$var" > /dev/tty

This writes to the console.
Is this what you want?

It appears that you want the contents of file name to be the stdin of the program (the < redirects the stdin of program and feeds the contents of the file into it), and you also want the name of the file to be a parameter available in program.

assuming that program expects a filename as the first parameter then

program filename <filename

should achieve what you want

The problem ist that $n (for n=1,2,...) denotes the first (second, ...) positional argument. In a construct like "program < file" "file" is not an argument but the "< file" creates an input stream consisting of the content of "file". A program presented with input this way will only recognize some input to its <stdin> but will not know where this is coming from. Otherwise one would have to make (different) provisions for the call "program < file" and "program < /dev/somedevice" or "program < <userinput>". But, contrary, the difference in working with a device, a file or whatever is taken care of by the operating system itself. It will take all these different data sources and turn them into "streams". Note, btw., that you can treat a file like a "clotted datastream" and a datastream like a file. This is one of the biggest avantages of UNIX designwise IMHO.

To the problem of the threadstarter:

You can always solve problems by adding another layer of indirection. ;-))

Seriously: create a small script which does only call the program itself. This script gets a positional parameter which would be the input files name:

#!/bin/ksh

fInput="$1"

print - "The input file is $fInput"

program < "$fInput"

exit $?

If your program awaits positional parameters too you have to modify this wrapper script a bit:

#!/bin/ksh

typeset fInput="$1"
typeset chOtherParms=""

shift
chOtherParms="$*"

print - "The input file is $fInput"

program $chOtherParms < "$fInput"

exit $?

If "program" is another script it would be even easier to modify it to accept an additional parameter "inputfile" and use it instead of using a pipeline. In this case post your script and we'll see how to do it if you can't do it by yourself.

I hope this helps.

bakunin

I am afraid I haven't given enough details about my case.
I will try to be more clear posting what I am doing.
I want to add the time information contained in the filename inside the file using the command:
sh AddTim < filename-0.5_s.dat > filename-0.5_s.out
where AddTim is:
#!/bin/sh
time=$(echo $1 | cut -d "-" -f2 | cut -d "_" -f1)
sed "s/X/X, TIME=$time/"

Unfortunately, the resulting time information is blank!

er, you are not passing any parameters to AddTim, you are redirecting it's input stream.
I take it that filename-0.5_s.dat contains a line with X in it somewhere, which you want replaced with X, TIME=0.5?

if so, then

sh AddTime filename-0.5_s.dat <filename-0.5_s.dat >filename-0.5_s.out

should achieve that.

The > filename-0.5_s.out
creates a blank file before anything else happens, like running your script.
I am assuming that the sed command read from stdin as well.

#!/bin/sh
# usage AddTim filename
#!/bin/sh
awk '{print $1; exit}' "$1" | read one
time=$(echo $one | cut -d "-" -f2 | cut -d "_" -f1)
sed "s/X/X, TIME=$time/" "$1" > tmp.tmp
mv tmp.tmp "$1" 

This parallels the logic in your code without using stdin. There are probably much better solutions. Wempy's solution will work but requires a manual rename of the resulting output file.

a slightly different approach:

#!/bin/bash
fn=$1
time=$(echo $fn | cut -d "-" -f2 | cut -d "_" -f1)
sed "s/X/X, TIME=$time/" $fn

call that with

script filename-0.5_s.dat >filename-0.5_s.out

Ok, lets go through it step by step, a few basics first:

"$1" is the first positional argument. If you call a program "program arg1 arg2" then "arg1" is the content of "$1", the content of "$2" would be "arg2", etc.. If you give only 2 arguments "$3", "$4", etc. will all be blank.

The positional arguments will never present any input to a program but are adjustments to its workings: you could give a filename to a program, but in itself this is not input! Input is when this file is read but to do this the program would have to open and read the file, thus creating the input. The filename itself is only a "calibration" directing this mechanism to the right file, not the input itself. This is an important difference.

Second: you can visualize a UNIX process to be like a garden hose: on one side you pour something in, inside happens something or not, on the other side something pours out. Consider the following command:

program1 | program2 > file

program1 creates some stream of data. It could be redirected to a file via "program1 > file", but firstly the data pours out of the <stdout> channel of program1. From there it would go to the screen per default, because this is the device this <stdout> is connected to per default. "program1 > file" would simply change this connection from screen to a file named "file". Instead of a file we use another redirection: the pipeline. The pipeline tells the OS to call another program "program2" and connect the <stdin> of this process with the <stdout> of the first process. Similar for the the second redirection, which redirects the <stdout> of program2 to some file.

To stay with our analogy: we have connected two garden hoses (say, a green one and a red one, because the programs are different ones) but inside both of these hoses there is no information about where the content running through them comes from. Also program2 knows nothing about what happens with its output, if it goes into another hose (program) or the dumpster (file).

So, while the pipeline is like the content of the hose, the parameters are dealing with the properties of the hoses themselves: lets say one hose enriches the content running through it with something and the positional parameter would be the adjustment knob controlling how much of the substance is mixed into the stream.

Now, after this rather lengthy theoretical explanation, back to your problem:

Of course the expression is blank because you gave the script no positional parameter - you didn't turn the adjustment knob - you just told the operating system where to get the input from and where to put the output at. This information - because being irrelevant - never reached the script.

But there is a solution, which wempy as well as i did give you: make the filename not only a redirection but also a positional parameter. You can do this by a wrapper script which takes care of the ambiguity so you have to enter the filename only once.

Let the code of AddTim stay like it is (i haven't checked if it is correct, but lets just suppose that it is) and write a second script, called AddTimWrapper. I supposed that your input files are all called ".dat" and the corresponding output files ".out", if this is not true just modify the script a bit:

#!/bin/sh

fInput="$1"
fOutput="${fInput%%.dat}.out"  # this is just a clever way to subract ".dat" and add ".out"

AddTim "$fInput" < "$fInput" > "$fOutput"

exit $?

Now call the script not by calling "AddTim" but by "AddTimWrapper file" where "file" is a file named "<something>.dat".

I hope this helps.

bakunin

thanks all of you guys for your replies.
I could now implement the time successfully.
It was also helpful to learn more about scripts.

$ cat filename-0.5_s.dat
X
$ { rm filename-0.5_s.dat&&sh AddTim "$_">filename-0.5_s.dat;}<filename-0.5_s.dat
$ cat filename-0.5_s.dat
X, TIME=0.5

And, of course, you don't need all that echo, cut ... commands, but that's another problem.

Or even:

{ rm filename-0.5_s.dat&&sh AddTim "$_">"$_";}<filename-0.5_s.dat