understanding I/O redirection in Unix - linux

I am not sure if I fully understand Redirection in Unix.
I was playing around with this command " cat hello.txt world.txt > world.txt "
The output I get is strange to me. I was guessing that world.txt will be empty initially and content of hello.txt will be stdout to world.txt.
When I run the " cat hello.txt world.txt > world.txt "
command
and later check the content in world.txt, I see the content of hello.txt being repeatedly copied.
For eg If hello.txt has the content "Hello World!", then world.txt will print
"Hello, world !" so many times like hundreds of lines.
Any reason why this happens
thank you

Your understanding of redirection is fine; it's your understanding of cat that is lacking. Your implementation of cat writes the contents of hello.txt to world.txt (which was indeed empty when cat started due to the redirection), then goes into the following loop:
Read from world.txt.
If no data was found, exit
Write data to world.txt,
GOTO 1
POSIX doesn't require such a loop; cat could (I think) simply read the entire contents of world.txt first, write it out to standard output, then exit without checking if world.txt has grown. But I suspect no implementation would ever actually do that, for performance reasons.

First, cat will open hello.txt and effectivley copy its contents to world.txt due to redirection. What's going to happen next however, is that cat will open world.txt, which is now a copy of hello.txt. As cat reads data from world.txt, it redirects its output to the designated file (i.e., world.txt), meaning there's always more to read (assuming infinite disk).

Related

How do you append a string built with interpolation of vars and STDIN to a file?

Can someone fix this for me.
It should copy a version log file to backup after moving to a repo directory
Then it automatically appends line given as input to the log file with some formatting.
That's it.
Assume existence of log file and test directory.
#!/bin/bash
cd ~/Git/test
cp versionlog.MD .versionlog.MD.old
LOGDATE="$(date --utc +%m-%d-%Y)"
read -p "MSG > " VHMSG |
VHENTRY="- **${LOGDATE}** | ${VHMSG}"
cat ${VHENTRY} >> versionlog.MD
shell output
virufac#box:~/Git/test$ ~/.logvh.sh
MSG > testing script
EOF
EOL]
EOL
e
E
CTRL + C to get out of stuck in reading lines of input
virufac#box:~/Git/test$ cat versionlog.MD
directly outputs the markdown
# Version Log
## version 0.0.1 established 01-22-2020
*Working Towards Working Mission 1 Demo in 0.1 *
- **01-22-2020** | discovered faker.Faker and deprecated old namelessgen
EOF
EOL]
EOL
e
E
I finally got it to save the damned input lines to the file instead of just echoing the command I wanted to enter on the screen and not executing it. But... why isn't it adding the lines built from the VHENTRY variable... and why doesn't it stop reading after one line sometimes and this time not. You could see I was trying to do something to tell it to stop reading the input.
After some realizing a thing I had done in the script was by accident... I tried to fix it and saw that the | at the end of the read command was seemingly the only reason the script did any of what it did save to the file in the first place.
I would have done this in python3 if I had know this script wouldn't be the simplest thing I had ever done. Now I just have to know how you do it after all the time spent on it so that I can remember never to think a shell script will save time again.
Use printf to write a string to a file. cat tries to read from a file named in the argument list. And when the argument is - it means to read from standard input until EOF. So your script is hanging because it's waiting for you to type all the input.
Don't put quotes around the path when it starts with ~, as the quotes make it a literal instead of expanding to the home directory.
Get rid of | at the end of the read line. read doesn't write anything to stdout, so there's nothing to pipe to the following command.
There isn't really any need for the VHENTRY variable, you can do that formatting in the printf argument.
#!/bin/bash
cd ~/Git/test
cp versionlog.MD .versionlog.MD.old
LOGDATE="$(date --utc +%m-%d-%Y)"
read -p "MSG > " VHMSG
printf -- '- **%s** | %s\n' "${LOGDATE}" "$VHMSG" >> versionlog.MD

Reading /proc/PID/maps of short-lived process

I have a binary that exits/segfaults shortly after executing. I am trying to read /proc/PID/maps of this binary before it exits but with no luck.
I've tried cat /proc/($./binary [input] & echo $!)/maps which should result in /proc/pidofprocess/maps but always get No such file or directory.
My goal is to see where things get loaded to on subsequent runs.
You solution cat /proc/$(<pipeline>)/maps won't call cat until the whole <pipeline> has ended (even if you use & within it), so you will never get your maps.
On the other hand,
<pipeline> & cat /proc/${!}/maps
will return immediately. Of course, as your binary exits immediately, cat may still run too late to capture /proc/${!}/maps
But you could try:
while true; do; ./binary [input] & cat /proc/${!}/maps ; done
This restarts a race between your binary and cat all day long, and sometimes cat may win (it does, in my case, with ls <nonexisting file> instead of ./binary 1 out of 30 times)
This prints a lot of garbage on your terminal, but you can collect the succesful maps by redirecting your cats output:
while true; do; ./binary [input] & cat /proc/${!}/maps >> mymaps; done
Elegant? No. Effective: I hope so!

Very weird redirection behavior

I execute a program which print some texts. I redirect the texts to file by using > but I cannot see any texts on the file. For example, if the program prints "Hello" I can see the result on the shell:
$ ./a.out arg
Hello
But after I redirect I cannot get any message hello on shell as well as the redirected file.
$ ./a.out arg > log.txt
(print nothing)
$ cat log.txt
(print nothing)
I have no idea what's going on. Is there someone who knows what's happening here? Or is there someone who suffered similar situation?
OS: Ubuntu 14.10, x86_64 arch, and the program is really chromium-browser rather than ./a.out. I edited its JavaScript engine (v8, which is included in chromium-browser) and I tried to print some logs with lots of texts. I tried to save it by redirection but it doesn't work.
Surely I checked whether > symbol work or not. It works as expected on other programs like echo, ls, and so on.
$ echo hello > hello.txt
$ cat hello.txt
hello
How can the messages just go away? I think it should be printed on stdout (or stderr) or file. But it just goes away when I use > symbol.
It is somewhat common for programs to check isatty(stdout) and display different output based on whether stdout is connected to a terminal or not. For example, ls will display file names in a tabular format if output is to a terminal, but display them strictly one per line otherwise. It does this to make it easy to parse its output when it's part of a pipeline.
Not having looked at Chrome's source code myself, this is speculation, but it's possible Chrome is performing this sort of check and changing its output based on where stdout is redirected to.
Try to use "2>" which should redirect stderr to file
Or you can also try to use "&>" which should redirect everything (stderr and stdout)
See more at http://tldp.org/HOWTO/Bash-Prog-Intro-HOWTO-3.html

how to print the ouput/error to a text file?

I'm trying to redirect(?) my standard error/output to a text file.
I did my research, but for some reason the online answers are not working for me.
What am I doing wrong?
cd /home/user1/lists/
for dir in $(ls)
do
(
echo | $dir > /root/user1/$dir" "log.txt
) > /root/Desktop/Logs/Update.log
done
I also tried
2> /root/Desktop/Logs/Update.log
1> /root/Desktop/Logs/Update.log
&> /root/Desktop/Logs/Update.log
None of these work for me :(
Help please!
Try this for the basics:
echo hello >> log.txt 2>&1
Could be read as: echo the word hello, redirecting and appending STDOUT to the file log.txt. STDERR (file descriptor 2) is redirected to wherever STDOUT is being pointed. Note that STDOUT is the default and thus there is no "1" in front of the ">>". Works on the current line only.
To redirect and append all output and error of all commands in a script, put this line near the top. It will be in effect for the length of the script instead of doing it on each line:
exec >>log.txt 2>&1
If you are trying to obtain a list of the files in /home/user1/lists, you do not need a loop at all:
ls /home/usr1/lists/ >Update.log
If you are attempting to run every file in the directory as an executable with a newline as its input, and collect the output from all these programs in Update.log, try this:
for file in /home/user1/lists/*; do
echo | "$file"
done >Update.log
(Notice how we avoid the useless use of ls and how there is no redirection inside the loop.)
If you want to create an empty file called *.log.txt for each file in the directory, you would do
for file in /home/user1/lists/*; do
touch "$(basename "$file")"log.txt
done
(Using basename to obtain the file name without the directory part avoids the cd but you could do it the other way around. Generally, we tend to avoid changing the directory in scripts, so that the tool can be run from anywhere and generate output in the current directory.)
If you want to create a file containing a single newline, regardless of whether it already exists or not,
for file in /home/user1/lists/*; do
echo >"$(basename "$file")"log.txt
done
In your original program, you redirect the echo inside the loop, which means that the redirection after done will not receive any output at all, so the created file will be empty.
These are somewhat wild guesses at what you might actually be trying to accomplish, but should hopefully help nudge you slightly in the right direction. (This should properly be a comment, I suppose, but it's way too long and complex.)

command line "cat >> " behavior

What does this command do cat t.txt >> t.txt ? let say the t.txt only have one line of text "abc123". I assume the output of "abc123" is appended to t.txt. So, I should have 2 lines of "abc123". However, it just going in to a infinite loop. It doesn't stop until I hit Control-C. Is this the expect behavior of >>?
cat program opens the file for reading, reads the file and writes to standard out.
>> is a shell append redirect.
What you are seeing is the following cycle:
cat reads a line from t.txt
cat prints the line to file
the line is appended to t.txt
cat tests if it is at the end of the file
That 4th step will always be false, because by the time the EOF check happens a new line has been written. cat waits because the write always happens first.
If you want to prevent that behavior, you can add a buffer in between:
$ cat t.txt | cat >> t.txt
In this way, the write occurs after cat t.txt checks for EOF
What you are trying to do by:
cat t.txt >> t.txt
is like telling your system to read t.txt line by line and append each line to t.txt. Or in better words, "append the file to itself". The file is being gradually filled up with repetitions of the original contents of the file -- the reason behind your infinite loop.
Generally speaking, try to stay away from reading and writing to the same file using redirections. Is it not possible to break this down to two steps -- 1. Read from file, output to a temporary file 2. append to the temporary file to the original file?
cat is a command in unix-like systems that concatenates multiple input files and sends their result to the standard output. If only one file is specified, it just outputs that one file. The >> part redirects the output to the given file name, in your case t.txt.
But what you have says, overwrite t.txt with the contents of itself. I don't think this behavior is defined, and so I'm not surprised that you have an infinite loop!

Resources