output redirection in UNIX - unix

I am a beginner in UNIX. I am finding some difficulty in input/output redirection.
ls -l >temp
cat temp
Here why temp file is shown in the list and moreover, it is showing 0 characters.
wc temp >temp
cat temp
here output is 0 0 0 temp.
Why lines, words, characters are 0.
Please help me to undestand this concept.

Because ls reads all the names and sorts them before printing anything, and because the output file is created before the command is executed, at the time when ls checks the size of temp, it is empty, so it shows up in the list as an empty file.
When wc reads the file, it is empty, so it reports 0 characters in 0 words on 0 lines, and writes this information into the file after it has finished reading the empty file.

When you pipe the output to a file, that file is created, the command is run (so ls lists it as an empty file, and wc counts the characters in the empty file), then the output is added to the file.
… in that order.

You cannot write and read from the same file at the same time.
So:
wc file > file # NOT WORKING
# but this works:
wc file > file.stats
mv file.stats file # if you want that

Related

Move files with certain name to folder with certain name - Unix

I am trying since a while now, can anyone help me please?
I want to move files with certain names, e.g.
tree.txt
apple.txt
....
To their corresponding folder
tree
apple
I tried this but it takes too much time to do it individually:
mv *tree* destination_directory/tree
because then I need to repeat this 200 times
mv *apple* destination_directory/apple
.....
Is there any way to make this faster?
I have a list.txt with all the file names.
Thank you so much,
Bine
Assuming toy have the list of txt files in a file called "filewithtxts", you can read the file into a while loop and then process each entry
while read file;
do
dir=$(awk -F_ '{ print $(NF-1)"_"$NF }' <<< "${file%.txt}")
mv *"${file%.txt}"* "destination_directory/$dir" # Use ${file%.txt} to strip .txt from the entry
done < filewithtxts

Converting Filename to Filename_Inode

I'm writing my first script that takes a file and moves it to another folder, except that I want to change the filename of the file to filename_inode instead of just filename incase there are any files with the same name
I've figured out how to show this by creating the following 4 variables
inode=$(ls -i $1 | cut -c1-7) #lists the file the user types, cuts the inode from it
space="_" #used to put inbetween the filename and bname
bname=$(basename $1) #gets the basename of the file without the directory etc
bnamespaceinode=$bname$space$inode #combines the 3 values into one variable
echo "$bnamespaceinode #prints filename_inode to the window
So the bottom echo shows filename_inode which is what I want, except now when I try to move this using mv or cp i'm getting the following errors
I dont think it's anything wrong with the syntax i'm using for the mv and cv commands, and so I'm thinking I need to concatenate the 3 variables into a new file or use the result of the first and then append the other 2 to that file?
I've tried both of the above but still not having any luck, any ideas?
Thanks
Without clearer examples, I guess this could work:
$TARGETDIR=/my/target/directory
mv $1 $TARGETDIR/$(basename "$1" | sed 's/_.*/_inode/')

Difference between cat filename and cat < filename in unix

Suppose I have a file named "file1". I want to display the contents of "file1" using the cat command in Unix.
Both cat file1 and cat < file1 are working similarly. What is the difference between them?
It's where input comes from.
If you say cat file1 the shell doesn't do anything special. cat calls open(2) on the file and reads from it
If you say cat < file1 the shell calls open(2) on the file and calls dup(2) into STDIN_FILENO for cat. cat just reads from STDIN_FILENO
We can use another command to notice the difference between:
wc –w food2.txt
Possible output:
6 food2.txt
the command tells the file name since it knows it (passed as an argument) .
wc –w < food2.txt
Possible output:
6
the standard input is redirected to file food2.txt without the command knowing about it .
cat opens a file, and cat > fileName tells the shell to open the file in the cat standard input.
Here is a link with more detailed information/answer:
https://unix.stackexchange.com/questions/258931/difference-between-cat-and-cat

Append a Command to the file in Unix

how do we append a command to the first line of the file?
for example a command to display date?Thank in advance for your answer
It is not possible to prepend directly to a file without overwriting the file.
This may work for you instead.
mv file1.txt file1.txt.tmp # Move file temporarily
date > file1.txt # Add your command
cat file1.txt.tmp >> file1.txt # Append original content
rm file1.txt.tmp # Remove temporary file

To replace the first character of the last line of a unix file with the file name

We need a shell script that retrieves all txt files in the current directory and for each file checks if it is an empty file or contains any data in it (which I believe can be done with wc command).
If it is empty then ignore it else since in our condition, all txt files in this directory will either be empty or contain huge data wherein the last line of the file will be like this:
Z|11|21||||||||||
That is the last line has the character Z then | then an integer then | then an integer then certain numbers of | symbols.
If the file is not empty, then we just assume it to have this format. Data before the last line are garbled and not necessary for us but there will be at least one line before the last line, i.e. there will be at least two lines guaranteed if the file is non-empty.
We need a code wherein, if the file is non-empty, then it takes the file, replaces the 'Z' in the last line with 'filename.txt' and writes the new data into another file say tempfile. The last line will thus become as:
filename.txt|11|21|||||||
Remaining part of the line remains same. From the tempfile, the last line, i.e., filename.txt|int|int||||| is taken out and merged into a finalfile. The contents of tempfile is cleared to receive data from next filename.txt in the same directory. finalfile has the edited version of the last lines of all non-empty txt files in that directory.
Eg: file1.txt has data as
....
....
....
Z|1|1|||||
and file2.txt has data as
....
....
....
Z|2|34|||||
After running the script, new data of file1.txt becomes
.....
.....
.....
file1.txt|1|1||||||
This will be written into a new file say temp.txt which is initially empty. From there the last line is merged into a file final.txt. So, the data in final.txt is:
file1.txt|1|1||||||
After this merging, the data in temp.txt is cleared
New data of file2.txt becomes
...
...
...
file2.txt|2|34||||||
This will be written into the same file temp.txt. From there the last line is merged into the same file final.txt.
So, the data in final.txt is
file1.txt|1|1||||||
file2.txt|2|34||||||
After considering N number of files that was returned to be as of type txt and non-empty and within the same directory, the data in final.txt becomes
file1.txt|1|1||||||
file2.txt|2|34||||||
file3.txt|8|3||||||
.......
.......
.......
fileN.txt|22|3|||||
For some of the conditions, I already know the command, like
For finding files in a directory of type text,
find <directory> -type f -name "*.txt"
For taking the last line and merging it into another file
tail -1 file.txt>>destination.txt
You can use 'sed' to replace the "z" character. You'll be in a loop, so you can use the filename that you have in that. This just removes the Z, and then echos the line and filename.
Good luck.
#!/bin/bash
filename=test.txt
line=`tail -1 $filename | sed "s/Z/$filename/"`
echo $line
Edit:
Did you run your find command first, and see the output? It has of course a ./ at the start of each line. That will break sed, since sed uses / as a delimiter. It also will not work with your problem statement, which does not have an extra "/" before the filename. You said current directory, and the command you give will traverse ALL subdirectories. Try being simple and using LS.
# `2>/dev/null` puts stderr to null, instead of writing to screen. this stops
# us getting the "no files found" (error) and thinking it's a file!
for filename in `ls *.txt 2>/dev/null` ; do
... stuff ...
done

Resources