Linux Write Something on multiple files - linux

I have a file "atest.txt" that have some text..
I want to print this text at files "asdasd.txt asgfaya.txt asdjfusfdgh.txt asyeiuyhavujh.txt"
This files is not exist on my server..
I'm running Debian.. What can i do?

Use the tee(1) command, which duplicates its standard input to standard output and any files specified on the command line. E.g.
printf "Hello\nthis is a test\nthank you\n"
| tee test1.txt test2.txt $OTHER_FILES >/dev/null
Using your example:
cat atest.txt
| tee asdasd.txt asgfaya.txt asdjfusfdgh.txt asyeiuyhavujh.txt >/dev/null

From your bash prompt:
for f in test1.txt test2.txt test3.txt; do echo -e "hello\nworld" >> $f; done
If the text lives in atest.txt then do:
for f in test1.txt test2.txt test3.txt; do cat atest.txt >> $f; done

Isn't it simply:
cp atest.txt asdasd.txt
cp atest.txt asgfaya.txt
cp atest.txt asdjfusfdgh.txt
cp atest.txt asyeiuyhavujh.txt
?

In bash you can write
#!/bin/bash
$TEXT="hello\nthis is a test\nthank you"
for i in `seq 1 $1`; do echo -e $TEXT >text$i.txt; done
EDIT (in response of question change)
If you can't determine programmatically the names of the target files then you can use this script it:
#!/bin/bash
ORIGIN=$1;
shift
for i in `seq $#`; do cp "$ORIGIN" "$1"; shift; done
you can use it this way:
script_name origin_file dest_file1 second_dest_file 'third file' ...
If you are wondering why there are the double quotes into the cp command, it is for cope with filename containing spaces

If anyone would like to write same thing to all files in dir:
printf 'your_text' | tee *

Related

Append in file a linux command

When doing
echo "some stuff" >> file.txt
I can append to my file some stuff
But when trying to do
echo ls >> file.txt
I append ls instead of listing all my items in the current path. How can I fix this? Sorry I'm new to linux
just redirect the ls output
ls >> file.txt
echo ls >> file.txt
This is interpreted as echo the term ls to file.txt. This isn't what you want.
>> redirects the output of a command, which can be ls.
So you could do:
ls >> file.txt

How can i move/group specific folders in bash?

I have a folder structure like the following:
2020-123-1
2020-123-2
2020-123-3
2020-124-1
2020-124-2
...
I need to create folders from the first 2 numbers and omit whatever follows the second dash (-). Then I need to put the prior folders under the newly created ones with the correct name.
2020-123
->2020-123-1
->2020-123-2
->2020-123-3
2020-124
->2020-124-1
->2020-124-2
I tried to write a script in bash like this:
ls -d */ > folder.txt
cut -f1,2 -d"-" folder.txt |cut -f1 -d"/" |sort|uniq > mainfolder.txt
while read line; do mkdir $line ; done < mainfolder.txt
while read line; do mv $(cut -f1,2 -d"-" $line) $line/ ; done < folder.txt
I couldn't make the last line work, I know it has issues.
Actually, you don't have to parse the directory names and build the hierarchy. You can make use of the -p option of mkdir, thus, an awk one-liner will do the job:
awk -F'-' '{top=$1 FS $2;printf "mkdir -p %s; mv %s %s\n",top, $0, top}' dir.txt
The output with your example:
mkdir -p 2020-123; mv 2020-123-1 2020-123
mkdir -p 2020-123; mv 2020-123-2 2020-123
mkdir -p 2020-123; mv 2020-123-3 2020-123
mkdir -p 2020-124; mv 2020-124-1 2020-124
mkdir -p 2020-124; mv 2020-124-2 2020-124
Note
This one-liner just print the commands without executing them, you just pipe the output to |sh if everything looks fine. Examine the output commands, change the printf format/values for adjustment.
I didn't quote the filenames, since your example doesn't contain any special chars. Do it if it is in the case.
So the final script is as follows:
ls -d */ | cut -f1 -d"/" > folder.txt
awk -F'-' '{top=$1 FS $2;printf "mkdir -p %s; mv %s %s\n",top, $0, top}' folder.txt |sh
In pure bash:
#!/bin/bash
for src in *-*-*; do
destdir=${src%-*}
[[ -d $destdir ]] || mkdir "$destdir" || exit
# This just prints out the command that will be called.
# Remove the "echo" in actual script after making sure it will run as intented
echo mv "$src" "$destdir"
done
In the script above it is assumed that each file name to be moved contains exactly two dashes. If it can contain two or more dashes then the destdir=${src%-*} line should be replaced with these two lines:
suffix=${src#*-*-}
destdir=${src%"-$suffix"}
For detailed information read the "shell parameter expansion" section in bash reference.
Additionally, a good read article is: Why you shouldn't parse the output of ls

Remove certain characters in a filename in Linux

I have files in a directory such as
FILE1.docx.txt
FILE2.docx.txt
FILE3.docx.txt
FILE4.docx.txt
FILE5.docx.txt
And I would like to remove .docx from all of them to make the final output such as
FILE1.txt
FILE2.txt
FILE3.txt
FILE4.txt
FILE5.txt
How do I do this?
With Parameter Expansion and mv
for f in *.docx.txt; do
echo mv -vn "$f" "${f%%.*}.${f##*.}"
done
The one-liner
for f in *.docx.txt; do echo mv -vn "$f" "${f%%.*}.${f##*.}"; done
Remove the echo if you think the output is correct, to rename the files.
Should work in any POSIX compliant shell, without any script.
With bash, enable the nullglob shell option so the glob *.docx.txt will not expand as literal *.docx.txt if there are no files ending with .docx.txt
#!/usr/bin/env bash
shopt -s nullglob
for f in *.docx.txt; do
echo mv -vn "$f" "${f%%.*}.${f##*.}"
done
UPDATE: Thanks to #Léa Gris add nullglob change the glob to *.docx.txt and add -n to mv, Although -n and -v is not defined by POSIX as per https://pubs.opengroup.org/onlinepubs/9699919799/utilities/mv.html It should be in both GNU and BSD mv
Just run this python script in the same folder that contains files:
import os
for file in os.listdir(os.getcwd()):
aux = file.split('.')
if len(aux) == 3:
os.rename(file, aux[0] + '.' + aux[2])
you can make use of sed and bash like this:
for i in *.docx.txt
do
mv "$i" "`echo $i | sed 's/.docx//'`"
done

Linux commands to copy one file to many files

Is there a one-line command/script to copy one file to many files on Linux?
cp file1 file2 file3
copies the first two files into the third. Is there a way to copy the first file into the rest?
Does
cp file1 file2 ; cp file1 file3
count as a "one-line command/script"? How about
for file in file2 file3 ; do cp file1 "$file" ; done
?
Or, for a slightly looser sense of "copy":
tee <file1 file2 file3 >/dev/null
just for fun, if you need a big list of files:
tee <sourcefile.jpg targetfiles{01-50}.jpg >/dev/null- Kelvin Feb 12 at 19:52
But there's a little typo. Should be:
tee <sourcefile.jpg targetfiles{01..50}.jpg >/dev/null
And as mentioned above, that doesn't copy permissions.
You can improve/simplify the for approach (answered by #ruakh) of copying by using ranges from bash brace expansion:
for f in file{1..10}; do cp file $f; done
This copies file into file1, file2, ..., file10.
Resource to check:
http://wiki.bash-hackers.org/syntax/expansion/brace#ranges
for FILE in "file2" "file3"; do cp file1 $FILE; done
You can use shift:
file=$1
shift
for dest in "$#" ; do
cp -r $file $dest
done
cat file1 | tee file2 | tee file3 | tee file4 | tee file5 >/dev/null
(no loops used)
To copy the content of one file (fileA.txt) to many files (fileB.txt, fileC.txt, fileD.txt) in Linux,
use the following combination cat and tee commands:
cat fileA.txt | tee fileB.txt fileC.txt fileD.txt >/dev/null
applicable to any file extensions
only file names and extensions change, everything else remains same.
Use something like the following. It works on zsh.
cat file > firstCopy > secondCopy > thirdCopy
or
cat file > {1..100} - for filenames with numbers.
It's good for small files.
You should use the cp script mentioned earlier for larger files.
I'd recommend creating a general use script and a function (empty-files), based on the script, to empty any number of target files.
Name the script copy-from-one-to-many and put it in your PATH.
#!/bin/bash -e
# _ _____
# | |___ /_ __
# | | |_ \ \/ / Lex Sheehan (l3x)
# | |___) > < https://github.com/l3x
# |_|____/_/\_\
#
# Copy the contents of one file to many other files.
source=$1
shift
for dest in "$#"; do
cp $source $dest
done
exit
NOTES
The shift above removes the first element (the source file path) from the list of arguments ("$#").
Examples of how to empty many files:
Create file1, file2, file3, file4 and file5 with content:
for f in file{1..5}; do echo $f > "$f"; done
Empty many files:
copy-from-one-to-many /dev/null file1 file2 file3 file4 file5
Empty many files easier:
# Create files with content again
for f in file{1..5}; do echo $f > "$f"; done
copy-from-one-to-many /dev/null file{1..5}
Create empty_files function based on copy-from-one-to-many
function empty-files()
{
copy-from-one-to-many /dev/null "$#"
}
Example usage
# Create files with content again
for f in file{1..5}; do echo $f > "$f"; done
# Show contents of one of the files
echo -e "file3:\n $(cat file3)"
empty_files file{1..5}
# Show that the selected file no longer has contents
echo -e "file3:\n $(cat file3)"
Don't just steal code. Improve it; Document it with examples and share it. - l3x
Here's a version that will preface each cp command with sudo:
#!/bin/bash -e
# Filename: copy-from-one-to-may
# _ _____
# | |___ /_ __
# | | |_ \ \/ / Lex Sheehan (l3x)
# | |___) > < https://github.com/l3x
# |_|____/_/\_\
#
# Copy the contents of one file to many other files.
# Pass --sudo if you want each cp to be perfomed with sudo
# Ex: copy-from-one-to-many $(mktemp) /tmp/a /tmp/b /tmp/c --sudo
if [[ "$*" == *--sudo* ]]; then
maybe_use_sudo=sudo
fi
source=$1
shift
for dest in "$#"; do
if [ $dest != '--sudo' ]; then
$maybe_use_sudo cp $source $dest
fi
done
exit
You can use standard scripting commands for that instead:
Bash:
for i in file2 file3 ; do cp file1 $i ; done
The simplest/quickest solution I can think of is a for loop:
for target in file2 file3 do; cp file1 "$target"; done
A dirty hack would be the following (I strongly advise against it, and only works in bash anyway):
eval 'cp file1 '{file2,file3}';'
Go with the fastest cp operations
seq 1 10 | xargs -P 0 -I xxx cp file file-xxx
it means
seq 1 10 count from 1 to 10
| pipe it xargs
-P 0 do it in parallel - as many as needed
-I xxx name of each input xargs receives
cp file file-xxx means copy file to file-1, file-2, etc
and if name of files are different here is the other solutions.
First have the list of files which are going to be created. e.g.
one
two
three
four
five
Second save this list on disk and read the list with xargs just like before but without using seq.
xargs -P 0 -I xxx cp file xxx < list
which means 5 copy operations in parallel:
cp file one
cp file two
cp file three
cp file four
cp file five
and for xargs here is the behind the scene (5 forks)
3833 pts/0 Ss 0:00 bash
15954 pts/0 0:00 \_ xargs -P 0 -I xxx cp file xxx < list
15955 pts/0 0:00 \_ cp file one
15956 pts/0 0:00 \_ cp file two
15957 pts/0 0:00 \_ cp file three
15958 pts/0 0:00 \_ cp file four
15959 pts/0 0:00 \_ cp file five
I don't know how correct this is but i have used something like this
echo ./file1.txt ./file2.txt ./file3.txt | xargs -n 1 cp file.txt
Where echo ./file1.txt ... is destination of a file and use it to feed xargs with one "destination" by one. Therefore command xargs -n 1. And lastly cp file.txt, which is self explanatory i think :)

Renaming a set of files in linux

I have 100 files named sub1.txt, sub2.txt,..., sub100.txt. I like to rename them as all1.txt, all2.txt,..., all100.txt. How can one do this in linux (unix)?
Thanks for your help.
The script below will rename an arbitrary number of file with the pattern sub*.txt. This also does a dry-run thanks to the echo. Simply remove the echo once you are satisfied with the results.
#!/bin/bash
for file in sub*.txt; do
echo mv "$file" "all${file#sub}"
done
Using the util-linux-ng version of rename:
rename sub all sub*.txt
Using the Perl script version of rename:
rename 's/^sub/all/' sub*.txt
for i in `seq 1 100`; do mv sub$i.txt all$i.txt; done
or
for i in sub*.txt; do j=`echo $i|sed -e s/sub/all/`; mv $i $j; done
for F in sub*.txt ; do mv $F all${F#sub}; done
Ruby(1.9+)
ruby -e 'Dir["sub*.txt"].each {|x| File.rename(x, x.gsub(/^sub/,"all") ) }'
There is always a new way to do it:
$ ls sub*.txt | tr -d "sub" | xargs -I{} mv sub{} all{}
Hope it helps.

Resources