cp command to overwrite the destination file which is a symbolic link - linux

Does cp command have any option to overwrite the destination file which is a symbolic link?
The problem is as follows:
[dthnguyen#dthnguyen test]$ ls -l
total 8
-rw-rw-r--. 1 dthnguyen dthnguyen 5 Feb 21 09:07 a.txt
lrwxrwxrwx. 1 dthnguyen dthnguyen 7 Feb 21 08:55 b.txt -> ./a.txt
-rw-rw-r--. 1 dthnguyen dthnguyen 5 Feb 21 08:55 c.txt
[dthnguyen#dthnguyen test]$ cp c.txt b.txt
After do the copy, a.txt has the content of c.txt, b.txt still links to a.txt. The expected result is a.txt holds the old content, b.txt is a new regular file that has the same content as c.txt.

Tell cp to remove it first.
cp --remove-destination c.txt b.txt

Related

How to create a patch file for text files only in a directory

I have a directory with hundreds of text files and object files. I had a copy of this directory somewhere else and I edited it and recompiled it. the object files are now different, but I want to generate a patch from the text files only. is there a way to do this or do I need to separate them into different folders?
diff -uraN original/ new/ > patch.diff
how can I specify file types in this command?
-X excludes, but I want the opposite of this. I want to exclude everything except .txt files
Did you want one diff per txt?
for f in original/*.txt # for each original
do d=${f#original/} # get base name
diff -uraN "$f" "new/$d" > ${d%.txt}.diff # diff old against new
done
You mention -X; I'm not sure how diff implements it, but the bash CLI allows extended globbing.
$: shopt -s extglob
$: ls -l *.???
-rw-r--r-- 1 P2759474 1049089 0 May 10 21:49 OCG3C82.tmp
-rw-r--r-- 1 P2759474 1049089 0 May 11 03:22 OCG511D.tmp
-rw-r--r-- 1 P2759474 1049089 0 May 12 00:03 OCG5214.tmp
-rw-r--r-- 1 P2759474 1049089 0 May 14 09:34 a.txt
-rw-r--r-- 1 P2759474 1049089 0 May 14 09:34 b.txt
$: ls *.!(txt)
-rw-r--r-- 1 P2759474 1049089 0 May 10 21:49 OCG3C82.tmp
-rw-r--r-- 1 P2759474 1049089 0 May 11 03:22 OCG511D.tmp
-rw-r--r-- 1 P2759474 1049089 0 May 12 00:03 OCG5214.tmp
If I understand your question correctly, you just want to perform the diff command on any files with the extension .txt.
Unfortunately, diff has no include option, but we can get around it by using find to get a list of the files which aren't text files, and then we can exclude those using -X. This one liner will do that.
find original new ! -name '*.txt' -printf '%f\n' -type f | diff -uraN original/ new/ -X - > patch.diff
If you want more info on how that works you can check out the man pages for find and diff.

Print the first line of each file inside a tar.gz without extracting

I'm looking for a command in order to print the first line of every file contained in a tar.gz archive, without extracting it.
Example:
tar -ztvf MyArchive.tar.gz
-rw-r--r-- root/root 3732541752 2020-04-04 03:24 FILE1.TXT
-rw-r--r-- root/root 90493394 2020-04-04 03:16 FILE2.TXT
-rw-r--r-- root/root 103294570 2020-04-03 21:06 FILE3.TXT
-rw-r--r-- root/root 16865694 2020-04-03 21:07 FILE4.TXT
-rw-r--r-- root/root 13176227988 2020-04-03 23:36 FILE5.TXT
I need to print the first line of each FILE*.TXT inside the tar.gz
How can I achieve this?
You could achieve using tar and for loop commands.
for i in $(tar -ztvf MyArchive.tar.gz|grep -i file|awk '{print $NF}')
do
tar xfO MyArchive.tar.gz $i|head -1
done
Using "tar xfO MyArchive.tar.gz filename" to read the content of files inside tar.gz
Try this:
tar zxf MyArchive.tar.gz --to-command="head -n 1"
This command takes files in the tar individually and feeds them into the command "head -n 1".

How to find out if ls command output is file or a directory Bash

ls command outputs everything that is contained in current directory. For example ls -la will output something like this
drwxr-xr-x 3 user user 4096 dec 19 17:53 .
drwxr-xr-x 15 user user 4096 dec 19 17:39 ..
drwxrwxr-x 2 user user 4096 dec 19 17:53 tess (directory)
-rw-r--r-- 1 user user 178 dec 18 21:52 file (file)
-rw-r--r-- 1 user user 30 dec 18 21:47 text (file)
And what if I want to know how much space does all files consume. For that I would have to sum $5 from all lines with ls -la | awk '{ sum+=$5 } END{print sum}'. So how can I only sum size of files and leave directories behind?
You can use the following :
find . -maxdepth 1 -type f -printf '%s\n' | awk '{s+=$1} END {print s}'
The find command selects all the files in the current directory and output their size. The awk command sums the integers and output the total.
Don't.
One of the most quoted pages on SO that I've seen is https://unix.stackexchange.com/questions/128985/why-not-parse-ls-and-what-do-to-instead.
That being said and as a hint for further development, ls -l | awk '/^-/{s+=$5} END {print s}' will probably do what you ask.

replacement on xargs variable returns empty string

I need to search for XML files inside a directory tree and create links for them on another directory (staging_ojs_pootle), naming these links with the file path (replacing slashes per dots).
the bash command is not working, I got stuck on the replacement part. Seems like the variable from xargs, named 'file', is not accessible inside the replacement code (${file/\//.})
find directory/ -name '*.xml' | xargs -I 'file' echo "ln" file staging_ojs_pootle/${file/\//.}
The replacement inside ${} result gives me an empty string.
Tried using sed but regular expressions were replacing all or just the last slash :/
find directory/ -name '*.xml' | xargs -I 'file' echo "ln" file staging_ojs_pootle/file |sed -e '/^ln/s/\(staging_ojs_pootle.*\)[\/]\(.*\)/\1.\2/g'
regards
Try this:
$ find directory/ -name '*.xml' |sed -r 'h;s|/|.|g;G;s|([^\n]+)\n(.+)|ln \2 staging_ojs_pootle/\1|e'
For example:
$ mkdir -p /tmp/test
$ touch {1,2,3,4}.xml
# use /tmp/test as staging_ojs_pootle
$ find /tmp/test -name '*.xml' |sed -r 'h;s|/|.|g;G;s|([^\n]+)\n(.+)|ln \2 /tmp/test/\1|e'
$ ls -al /tmp/test
total 8
drwxr-xr-x. 2 root root 4096 Jun 15 13:09 .
drwxrwxrwt. 9 root root 4096 Jun 15 11:45 ..
-rw-r--r--. 2 root root 0 Jun 15 11:45 1.xml
-rw-r--r--. 2 root root 0 Jun 15 11:45 2.xml
-rw-r--r--. 2 root root 0 Jun 15 11:45 3.xml
-rw-r--r--. 2 root root 0 Jun 15 11:45 4.xml
-rw-r--r--. 2 root root 0 Jun 15 11:45 .tmp.test.1.xml
-rw-r--r--. 2 root root 0 Jun 15 11:45 .tmp.test.2.xml
-rw-r--r--. 2 root root 0 Jun 15 11:45 .tmp.test.3.xml
-rw-r--r--. 2 root root 0 Jun 15 11:45 .tmp.test.4.xml
# if don NOT use the e modifier of s command, we can get the final command
$ find /tmp/test -name '*.xml' |sed -r 'h;s|/|.|g;G;s|([^\n]+)\n(.+)|ln \2 /tmp/test/\1|'
ln /tmp/test/1.xml /tmp/test/.tmp.test.1.xml
ln /tmp/test/2.xml /tmp/test/.tmp.test.2.xml
ln /tmp/test/3.xml /tmp/test/.tmp.test.3.xml
ln /tmp/test/4.xml /tmp/test/.tmp.test.4.xml
Explains:
for each xml file, use h to keep the origin filename in hold space.
the use s|/|.|g to substitute all / to . for xml filename.
use G to append the hold space to pattern space, then pattern space is CHANGED_FILENAME\nORIGIN_FILENAME.
use s|([^\n]+)\n(.+)|ln \2 staging_ojs_pootle/\1|e' to merge the command with CHANGED_FILENAME and ORIGIN_FILENAME, then use e modifier of s command to execute the command assembled above, which will do the actual works.
Hope this helps!
If you can be sure that the names of your XML files do not contain any word-splitting characters, you can use something like:
find directory -name "*.xml" | sed 'p;s/\//./' | xargs -n2 echo ln

Failed to change ownership of a file with permission 0666 in Linux

OS: Linux. (CentOS 6)
Step 1: login as normal user and change the directory to the home directory
Step 2: su as root
Step 3: create a file and change permission to 0666
Step 4: change file ownership as normal user but failed
[belcon#no1ca4sh ~]$ pwd
/home/belcon
[belcon#no1ca4sh ~]$ su
Password:
[root#no1ca4sh belcon]# touch test.txt
[root#no1ca4sh belcon]# echo "test">test.txt
[root#no1ca4sh belcon]# cat test.txt
test
[root#no1ca4sh belcon]# chmod 666 test.txt
[root#no1ca4sh belcon]# ls -l test.txt
-rw-rw-rw- 1 root root 5 Jun 26 17:50 test.txt
[root#no1ca4sh belcon]# exit
exit
[belcon#no1ca4sh ~]$ ls -l test.txt
-rw-rw-rw- 1 root root 5 Jun 26 17:50 test.txt
[belcon#no1ca4sh ~]$ chown belcon test.txt
chown: changing ownership of `test.txt': Operation not permitted
That doesn't make sense since I can copy that file to another temporary file as normal user. That temporary file's owner is normal user. Then I can delete the original file, and make a copy the temporary file with same name as original file created by root user. It is actually what 'chown' want to do.
[belcon#no1ca4sh ~]$ cp test.txt test1.txt
[belcon#no1ca4sh ~]$ ls -l test.txt test1.txt
-rw-r--r-- 1 belcon wheel 5 Jun 26 17:56 test1.txt
-rw-rw-rw- 1 root root 5 Jun 26 17:50 test.txt
[belcon#no1ca4sh ~]$ diff -Naur test1.txt test.txt
[belcon#no1ca4sh ~]$ rm test.txt
[belcon#no1ca4sh ~]$ ls -l test.txt test1.txt
ls: cannot access test.txt: No such file or directory
-rw-r--r-- 1 belcon wheel 5 Jun 26 17:56 test1.txt
[belcon#no1ca4sh ~]$ cp test1.txt test.txt
[belcon#no1ca4sh ~]$ ls -l test.txt test1.txt
-rw-r--r-- 1 belcon wheel 5 Jun 26 17:56 test1.txt
-rw-r--r-- 1 belcon wheel 5 Jun 26 17:57 test.txt
[belcon#no1ca4sh ~]$ diff -Naur test1.txt test.txt
[belcon#no1ca4sh ~]$
Can anyone please to explain why I couldn't change ownership of a file with permission 0666? Does there exist some reasons for that?
Ordinary users can't chown files.
See: https://unix.stackexchange.com/questions/27350/why-cant-a-normal-user-chown-a-file
Basically, it would allow users to evade quotas, and there are other edge cases where security can be compromised. (e.g. applications assuming root-owned files are secure, because only root could have written them).

Resources