CVS Tagging recursively from within a shell script - linux

My team uses CVS for revision control.I need to develop a shell script which extracts the content from a file and does a CVS tag to all .txt files(also the text files present in the sub-directories of the current direcotry) with that content. The file from which the content is extracted ,the script ,both are present in the same directory.
I tried running the script :
#!bin/bash
return_content(){
content=$(cat file1)
echo $content
}
find . -name "*.txt" -type f -print0|grep - v CVS|xargs -0 cvs tag $content
file1=> the file from where the content is extracted
"abc"=> content inside file1
Output:
abc
find: paths must precede expression
Usage: find [path...] [expression]
cvs tag: in directory
cvs [tag aborted]: there is no version here; run 'cvs checkout' first
I cannot figure out the problem. Please help

There are a few problems with the script.
1) The shebang line is missing the root /.
You have #!bin/bash and it should be #!/bin/bash
2) the -v option to grep has a space between the - and the v (and it shouldn't)
3) You don't actually call the return_content function in the last line - you refer to a variable inside the function. Perhaps the last line should look like:
find . -name "*.txt" -type f -print0|grep -v CVS|\
xargs -0 cvs tag $( return_content )
4) even after fixing all that, you may find that the grep complains because the print0 is passing it binary data (there are embedded nulls due to the -print0), and grep is expecting text. You can use more arguments to the find command to perform the function of the grep command and cut grep out, like this:
find . -type d -name CVS -prune -o -type f -name "*.txt" -print0 |\
xargs -0 cvs tag $( return_content )
find will recurse through all the entries in the current directory (and below), discarding anything that is a directory named CVS or below, and of the rest it will choose only files named *.txt.
I tested my version of that line with:
find . -type d -name CVS -prune -o -type f -name "*.txt" -print0 |\
xargs -t -0 echo ls -la
I created a couple of files with spaces in the names and .txt extensions in the directory so the script would show results:
bjb#spidy:~/junk/find$ find . -type d -name CVS -prune -o \
-type f -name "*.txt" -print0 | xargs -t -0 ls -la
ls -la ./one two.txt ./three four.txt
-rw-r--r-- 1 bjb bjb 0 Jun 27 00:44 ./one two.txt
-rw-r--r-- 1 bjb bjb 0 Jun 27 00:45 ./three four.txt
bjb#spidy:~/junk/find$
The -t argument makes xargs show the command it is about to run. I used ls -la instead of cvs tag - it should work similarly for cvs.

Related

Recursively go through directories and extract files one directory up CLI

Currently, I am using this
unrar e -r *.rar
To extract files, however this puts everything in my root unraring folder. Current structure
/HomeFolder
/Nextlevel
/RarFolder
rarfile.rar
I want the output to be
/HomeFolder
/Nextlevel
raroutput.ext
How would I do change my command to do this?
Try to use following approach:
find /HomeFolder -type d -name 'RarFolder' -printf '%h\n' | xargs -I{} sh -c 'cd {}; unrar e -r *.rar'
It searches for all nested 'RarFolder' subdirectories in '/HomeFolder', running your unrar command from within subdir containing 'RarFolder' (i.e. '/HomeFolder/Nextlevel' in your example).
To extract files to parent directory of one, containing '*.rar' files, command can be adjusted as:
find /HomeFolder -type f -name '*.rar' -printf '%h\n' | xargs -I{} sh -c 'cd {}/..; unrar e -r *.rar'

cat files in subdirectories using linux commands

I have the following directories:
P922_101
P922_102
.
.
Each directory, for instance P922_101 has following subdirectories:
140311_AH8MHGADXX 140401_AH8CU4ADXX
Each subdirectory, for instance 140311_AH8MHGADXX has the following files:
1_140311_AH8MH_P922_101_1.fastq.gz 1_140311_AH8MH_P922_101_2.fastq.gz
2_140311_AH8MH_P922_101_1.fastq.gz 2_140311_AH8MH_P922_101_2.fastq.gz
And files in 140401_AH8CU4ADXX are:
1_140401_AH8CU_P922_101_1.fastq.gz 1_140401_AH8CU_P922_4001_2.fastq.gz
2_140401_AH8CU_P922_101_1.fastq.gz 2_140401_AH8CU_P922_4001_2.fastq.gz
I want to do 'cat' for the files in the subdirectories in the following way:
cat 1_140311_AH8MH_P922_101_1.fastq.gz 2_140311_AH8MH_P922_101_1.fastq.gz
1_140401_AH8CU_P922_101_1.fastq.gz 2_140401_AH8CU_P922_101_1.fastq.gz > P922_101_1.fastq.gz
which means that files ending with _1.fastq.gz should be concatenated into a single file and files ending with _2.fatsq.gz into another file.
It should be run for all files in subdirectories in all directories. Could someone give a linux solution to do this?
Since they're compressed, you should probably use gzip -dc (decompress and write to stdout) -
find /somePath -type f -name "*.fastq.gz" -exec gzip -dc {} \; | \
tee -a /someOutFolder/out.txt
You can use find for this:
find /top/path -mindepth 2 -type f -name "*_1.fastq.gz" -exec cat {} \; > one_file
find /top/path -mindepth 2 -type f -name "*_2.fastq.gz" -exec cat {} \; > another_file
This will look for all the files starting from /top/path and having a name matching the pattern _1.fastq.gz / _2.fastq.gz and cat them into the desired file. -mindepth 2 makes find look for files that are at least under the current directory; this way, files in /top/path won't be matched.
Note that you will probably need zcat instead of cat, for gz files.
As you keep adding details in comments, let's see what else we can do:
Say you have the list of directories in a file directories_list, each line containing one:
while read directory
do
find $directory -mindepth 2 -type f -name "*_1.fastq.gz" -exec cat {} \; > $directory/output
done < directories_list

In Linux terminal, how to delete all files in a directory except one or two

In a Linux terminal, how to delete all files from a folder except one or two?
For example.
I have 100 image files in a directory and one .txt file.
I want to delete all files except that .txt file.
From within the directory, list the files, filter out all not containing 'file-to-keep', and remove all files left on the list.
ls | grep -v 'file-to-keep' | xargs rm
To avoid issues with spaces in filenames (remember to never use spaces in filenames), use find and -0 option.
find 'path' -maxdepth 1 -not -name 'file-to-keep' -print0 | xargs -0 rm
Or mixing both, use grep option -z to manage the -print0 names from find
In general, using an inverted pattern search with grep should do the job. As you didn't define any pattern, I'd just give you a general code example:
ls -1 | grep -v 'name_of_file_to_keep.txt' | xargs rm -f
The ls -1 lists one file per line, so that grep can search line by line. grep -v is the inverted flag. So any pattern matched will NOT be deleted.
For multiple files, you may use egrep:
ls -1 | grep -E -v 'not_file1.txt|not_file2.txt' | xargs rm -f
Update after question was updated:
I assume you are willing to delete all files except files in the current folder that do not end with .txt. So this should work too:
find . -maxdepth 1 -type f -not -name "*.txt" -exec rm -f {} \;
find supports a -delete option so you do not need to -exec. You can also pass multiple sets of -not -name somefile -not -name otherfile
user#host$ ls
1.txt 2.txt 3.txt 4.txt 5.txt 6.txt 7.txt 8.txt josh.pdf keepme
user#host$ find . -maxdepth 1 -type f -not -name keepme -not -name 8.txt -delete
user#host$ ls
8.txt keepme
Use the not modifier to remove file(s) or pattern(s) you don't want to delete, you can modify the 1 passed to -maxdepth to specify how many sub directories deep you want to delete files from
find . -maxdepth 1 -not -name "*.txt" -exec rm -f {} \;
You can also do:
find -maxdepth 1 \! -name "*.txt" -exec rm -f {} \;
In bash, you can use:
$ shopt -s extglob # Enable extended pattern matching features
$ rm !(*.txt) # Delete all files except .txt files

Capture the output of command line in a variable in Unix

I want to capture the output of the command below in a variable.
Command:
find . -iname 'FIL*'.TXT
The output is :
./FILE1.TXT
I want to capture './FILE1.TXT' into 'A' variable. But when I am trying
A=`find . -iname 'FIL*'.TXT`
then this command is displaying the data of the file. But I want ./FILE1.TXT value in the variable A.
# ls *.txt
test1.txt test.txt
# find ./ -maxdepth 1 -iname "*.txt"
./test1.txt
./test.txt
# A=$(find ./ -maxdepth 1 -iname "*.txt")
# echo $A
./test1.txt ./test.txt
You can ignore -maxdepth 1 if you want to. I had to use it for this example.
Or with a single file:
# ls *.txt
test.txt
# find ./ -maxdepth 1 -iname "*.txt"
./test.txt
# A=$(find ./ -maxdepth 1 -iname "*.txt")
# echo $A
./test.txt
Do you try ?
A="`find . -iname 'FIL*'.TXT`"
and
A="`find . -iname 'FIL*'.TXT -print`"
A file does not have any value, but does have a content. Use the following to display that content.
find . -iname 'FIL*'.TXT -exec cat {} \;
If you want all the contents (of all such files) in a variable, then
A=$(find . -iname 'FIL*'.TXT -exec cat {} \;)
BTW you could have used
find . -iname 'FIL*.TXT' -print0 | xargs -0 cat
If you want the names of such files in a variable, try
A=$(find . -iname 'FILE*.txt' -print)
BTW, on some several recent interactive shells (zsh, bash version 4 but not earlier versions) just write
A=**/FILE*.txt
My feeling is that the ** feature is by itself worth switching to a newer shell, but it is just my opinion.
Also, don't forget that files may have several or no names. Read about inodes ...

Find and rename a directory

I am trying to find and rename a directory on a linux system.
the folder name is something like : thefoldername-23423-431321
thefoldername is consistent but the numbers change every time.
I tried this:
find . -type d -name 'thefoldername*' -exec mv {} newfoldername \;
The command actually works and rename that directory. But I got an error on terminal saying that there is no such file or directory.
How can I fix it?
It's a harmless error which you can get rid of with the -depth option.
find . -depth -type d -name 'thefoldername*' -exec mv {} newfoldername \;
Find's normal behavior is to process directories and then recurse into them. Since you've renamed it find complains when it tries to recurse. The -depth option tells find to recurse first, then process the directory after.
It's missing the -execdir option! As stated in man pages of find:
-execdir command {};
Like -exec, but the specified command is run from the subdirectory containing the matched file, which is not normally the directory in which you started find.
find . -depth -type d -name 'thefoldername*' -execdir mv {} newfoldername \;
With the previous answer my folders contents are disappeared.
This is my solution. It works well:
for i in find -type d -name 'oldFolderName';
do
dirname=$(dirname "$i")
mv $dirname/oldFolderName $dirname/newFolderName
done
.../ABC -> .../BCD
find . -depth -type d -name 'ABC' -execdir mv {} $(dirname $i)/BCD \;
Replace 1100 with old_value and 2200 with new_value that you want to replace.
example
for i in $(find . -type d -iname '1100');do echo "mv "$i" "$i"__" >> test.txt; sed 's/1100__/2200/g' test.txt > test_1.txt; bash test_1.txt ; rm test*.txt ; done
Proof
[user#server test]$ ls -la check/
drwxr-xr-x. 1 user user 0 Jun 7 12:16 1100
[user#server test]$ for i in $(find . -type d -iname '1100');do echo "mv "$i" "$i"__" >> test.txt; sed 's/1100__/2200/g' test.txt > test_1.txt; bash test_1.txt ; rm test*.txt ; done
[user#server test]$ ls -la check/
drwxr-xr-x. 1 user user 0 Jun 7 12:16 2200
here __ in sed is used only to change the name it have no other significance

Resources