Bash script: Recognizing paths drag-and-dropped to konsole - linux

I was looking to make script that can copy user files within a Windows user directory to a backup drive. I pretty much want everything except Appdata to be transferred. I made a real simple script, but since the folders I am transferring to have spaces in the name (ex. '/media/gage/Backup\ Drive/'), it says that Drive' does not exist.
I am trying to drag&drop/paste the directory from a file manager onto the terminal and it ends up having 's around the entire path once I drag it over. Is there any way to have the input recognize the file names with the 's around it?
Here's what I have so far (I'm really new to bash scripts)
#!/bin/bash
echo "Enter the full path to the user's directory"
read srcName
echo "Enter the full path to the backup directory"
read dstName
echo "Copying from Users to Backup"
cd $srcName
cp -rp Documents $dstName
cp -rp Pictures $dstName
cp -rp Desktop $dstName
cp -rp Music $dstName
cp -rp Videos $dstName
cp -rp Downloads $dstName
cp -rp Favorites $dstName
Any help is appreciated
Thanks.

Terminals predate both drag&drop and copy&paste, so neither is integrated in a robust way. It's up to each terminal emulator to decide what to do.
Here's how some common ones react when you drag&drop one or more files from a graphical file manager:
xterm does nothing.
gnome-terminal pretends you typed the paths as space separated, shell escaped words, each of which is entirely single quoted with appropriate escapes:
'/path/foo' '/path/Rock Lobster - B-52'\''s.mp3'
konsole pops up a menu to let you choose between copy/move to the current directory of the shell, or to paste the paths as shell escaped words, which are only single quoted if they contain metacharaters:
/path/foo '/path/Rock Lobster - B-52'\''s.mp3'
From what you're describing, you're using gnome-terminal and just didn't try to drag&drop a file containing single quotes to see what else it does to the filename.
So what can you do?
I would recommend you just require that the path be copy-pasted verbatim, rather than drag&dropping files. This is how every other program works, and what you can do with Charles Duffy's solution.
To copy-paste the path as a string rather than dragging a file, you can usually open a Properties or Details tab in the file manager and copy the full path from there.
However, for fun, here's how you could interpret input as drag&dropped files from a file manager if you really wanted to, by populating an array using eval:
#!/bin/bash
echo "Drag&Drop files/dirs and press enter when done."
echo "Do not drag&drop/paste/type text, because it will be evaluated as code."
IFS="" read -r input
eval "files=( $input )"
echo "Here are the things you pasted:"
for file in "${files[#]}"
do
ls -ld "$file"
done
which runs like this:
$ ./test
Drag&Drop files/dirs and press enter when done.
Do not drag&drop/paste/type text, because it will be evaluated as code.
'/usr/local/home/me/Documents' '/usr/local/home/me/Downloads'
Here are the things you pasted:
drwxr-x--- 3 me eng 4096 Aug 7 13:46 /usr/local/home/me/Documents
drwxr-x--- 2 me eng 4096 Aug 17 14:32 /usr/local/home/me/Downloads

Related

Scripting file creation with specific extension

I am trying to do some basic scripting in linux (I am a recent transfer from windows) and I am simply trying to open a directory, create either the .odt or .odp files and then open them in their default programs.
I have tried to use "cat > filename.odt" but then i dont know how to stop the writing processes and proceed to next command.
#!/bin/bash
read -p "What would you like the file name to be: " name
cat > "$name".odt
xdg-open "$name.odt"
I want to just create the odt or odp file and then open it in either of their libre programs.
If the file is supposed to be blank when you create it you can just use: touch "$name".odt rather than cat. Also you don't need the quotes around the .odt in your last line. Your new file would look like this:
#!/bin/bash
read -p "What would you like the file name to be: " name
touch "$name".odt
xdg-open "$name".odt

Linux console equivalent to gui copy/paste file/directory scenario

How to simply recreate copy/paste functionality like in gui environments?
My typical scenario for copying file/directory in Linux console is:
cp source_path target_path
Sometimes paths are relative, sometimes absolute, but I need to provide them both. It works, but there are situations where I would like to recreate scenario from gui which is:
1. go to source directory
2. copy file/directory
3. go to target directory
4. paste file/directory
I imagine something like
cd source_directory_path
copy_to_stash source_name
cd target_directory_path
paste_from_stash [optional_new_target_name]
I know that there is a xclip app, but a documentation says that it copies content of a file, not a file handle. Also, I can use $OLDPWD variable and expand it when I copy file, but this is not a solution without some cumbersome.
Is there some simple, general, keyboard only, not awkward to use equivalent?
I've also asked the same question on superuser and answer that I've received is good enough for me.
In short: two additional scripts and temporary variable to hold intermediate value.
Below is a code and link to original answer.
#!/bin/bash
# source me with one of:
# source [file]
# . [file]
# Initialize
sa_file=
sa(){
# Fuction to save a file in the current PWD
if [[ -e "$PWD/$1" ]]; then
sa_file=$PWD/$1
echo "Saved for later: $sa_file"
else
echo "Error: file $PWD/$1 does not exist"
fi
}
pa(){
# Paste if file exists, to $1 if exists
if [[ -e "$sa_file" ]]; then
if [[ $1 ]]; then
cp -v "$sa_file" "$1"
else
cp -v "$sa_file" .
fi
else
echo "Error: file $sa_file does not exist, could not copy"
fi
}
https://superuser.com/a/1405953/614464
The way I see it your only option is to write a script to do all of those steps. You could easily implement the clipboard functionality by copying the file to the /tmp directory before copying again from it.
This should work as intended.
Usage: script [from] [to]
filename=$(basename "$0")
tmpfile=/tmp/$filename.$RANDOM
cd $(dirname "$0")
cp $tmpfile $filename
cd $(dirname "$1")
cp $tmpfile $(basename "$1")
One option: you can either copy-paste the filename using mouse, using copy-paste feature from your terminal emulator (e.g. Konsole or GNOME Terminal), but this: 1) requires a GUI since the terminal emulator software run in GUI; 2) well, requires a mouse.
Another option: utilize shell tab completion. You still need to type the filename, but not all of it.
Third option, and this is closer to how you work in a GUI file explorer: use a TUI-based file explorer, e.g. the dual-pane style Midnight Commander. You can use arrow keys (if you turn on the Lynx-like motion setting, which is very recommended) to quickly navigate the directory tree. Then select files using the Insert, +, -, or * keys, then copy/move files from one pane to another. It's very convenient. In fact half of the time I spend in CLI, I spend in MC.

General file paths for replicating shell scripts on any linux OS

I'm doing a shell script that is intended to be replicated on any user directory. The same script should be able to recognize any user's directories structure. Since I don't know the directory structure of every user, I wasn't able to recognize the parent folder to run some commands inside my script (Find command in the script). I would appreciate so much your help.
As you can see in my code below, I have three different types of paths ~/Desktop/input_folder/source.txt, ~/Desktop/output_folder/FILE_${array[$i]}_${array[$((i+1))]}.txt, and ../shares/path/FOLDER_WITH_MANY_FILES.
The third path has a route that I don't know where is located, so I used ../ to tell the script that assume the parent folder. For the first and second route, I used ~/because the route is in /home/username/. Am I doing it right? Do these routes need double commas ("/path/blah") in order to be read it by shell? I would appreciate your help.
Thanks!
My code:
#!/bin/bash
#source file location
input=$(cat ~/Desktop/input_folder/source.txt )
#read file
IFS=',' read -r -a array <<< "$input"
for((i=0;i<${#array[#]}-1;i+=2));do
#Create the file
touch ~/Desktop/output_folder/FILE_${array[$i]}_${array[$((i+1))]}.txt
echo "Search result for these parameters [${array[$i]},${array[$((i+1))]}]: "$'\r' >> ~/Desktop/output_folder/FILE_${array[$i]}_${array[$((i+1))]}.txt
#Find pattern and save it in file
find ../shares/path/FOLDER_WITH_MANY_FILES -name "*${array[$((i+1))]}*" -exec grep -l "${array[$i]}" {} + | tee -a ~/Desktop/output_folder/FILE_${array[$i]}_${array[$((i+1))]}.txt

RH Linux Bash Script help. Need to move files with specific words in the file

I have a RedHat linux box and I had written a script in the past to move files from one location to another with a specific text in the body of the file.
I typically only write scripts once a year so every year I forget more and more... That being said,
Last year I wrote this script and used it and it worked.
For some reason, I can not get it to work today and I know it's a simple issue and I shouldn't even be asking for help but for some reason I'm just not looking at it correctly today.
Here is the script.
ls -1 /var/text.old | while read file
do
grep -q "to.move" $file && mv $file /var/text.old/TBD
done
I'm listing all the files inside the /var/text.old directory.
I'm reading each file
then I'm grep'ing for "to.move" and holing the results
then I'm moving the resulting found files to the folder /var/text.old/TBD
I am an admin and I have rights to the above files and folders.
I can see the data in each file
I can mv them manually
I have use pwd to grab the correct spelling of the directory.
If anyone can just help me to see what the heck I'm missing here that would really make my day.
Thanks in advance.
UPDATE:
The files I need to move do not have Whitespaces.
The Error I'm getting is as follows:
grep: 9829563.msg: No such file or directory
NOTE: the file "982953.msg" is one of the files I need to move.
Also note: I'm getting this error for every file in the directory that I'm listing.
You didn't post any error, but I'm gonna take a guess and say that you have a filename with a space or special shell character.
Let's say you have 3 files, and ls -1 gives us:
hello
world
hey there
Now, while splits on the value of the special $IFS variable, which is set to <space><tab><newline> by default.
So instead of looping of 3 values like you expect (hello, world, and hey there), you loop over 4 values (hello, world, hey, and there).
To fix this, we can do 2 things:
Set IFS to only a newline:
IFS="
"
ls -1 /var/text.old | while read file
...
In general, I like setting IFS to a newline at the start of the script, since I consider this to be slightly "safer", but opinions on this probably vary.
But much better is to not parse the output of ls, and use for:
for file in /var/text.old/*`; do
This won't fork any external processes (piping to ls to while starts 2), and behaves "less surprising" in other ways. See here for some examples.
The second problem is that you're not quoting $file. You should always quote pathnames with double quoted: "$file" for the same reasons. If $file has a space (or a special shell character, such as *, the meaning of your command changes:
file=hey\ *
mv $file /var/text.old/TBD
Becomes:
mv hey * /var/text.old/TBD
Which is obviously very different from what you intended! What you intended was:
mv "hey *" /var/text.old/TBD

Is it possible to edit a symlink with a text editor?

When we create a symlink, the number of bytes the symlink takes up is exactly the length of the origin it points to. For instance,
$ ln -s dest link1
$ ln -s longer_dest link2
$ ls -l
lrwxrwxrwx 1 username 4 Mar 26 20:21 link1 -> dest
lrwxrwxrwx 1 username 11 Mar 26 20:21 link2 -> longer_dest
where link1 takes up 4 bytes, which is the length of dest; link2 takes up 11 bytes, which is the length of longer_dest. Therefore, symlinks are in fact no more than the destination path stored in plain text. So I am wondering if it is possible to edit (the destination) of a symlink in text editors, preferably Emacs. I googled for a while and couldn't find anyone talking about this. Note that this question is purely out of curiosity; I know full well that I can overwrite a symlink by ln -f -s.
Yes, in Emacs this is possible in dired-mode, specifically wdired (writable dired) mode.
Note, dired and wdired both are built-in packages.
Here's an example...
(BTW: I'm using Smex to give Emacs M-x command search & execute a more ergonomic UI + fuzzy matching)
It's possible in principle, but the editor would need to specifically support it, since reading the destination of a symlink requires a special system call: readlink().
You're unlikely to find any editors that actually do this, since it's not very useful, and conflicts with what most users want the editor to do when asked to open a symlink: open the file that it points to.
As per the Storage of symbolic links section in Wikipedia's article Symbolic Links, the symlinks are stored in an inode. This inode is a data structure containing several information about the file in question - as per this thread, the touch command can be used to change some of its values. So, it may not be possible to modify it by using a text editor, due to the problems that #Wyzard mentioned, but it might be modifiable by using some other command-line tools like touch.
I hope this helps!
It's not possible directly, as others have already pointed out, but of course you can write a script for it. Here's one I came up with when I had to change lots of symlinks
#! /bin/bash
tmp=$(mktemp)
trap "rm $tmp" EXIT
while [ ! -z "$1" ]; do
filename="$1"; shift
if [ ! -h "$filename" ]; then
echo "Not a symlink: $filename";
continue
fi
stat -c "%N" "$filename" >> $tmp
done
emacs $tmp
while read filename linkname; do
ln -sf "$linkname" "$filename"
done < <(sed "s/'\(.*\)' -> '\(.*\)'/\1 \2/" $tmp)
It worked for me, but it's certainly not perfect, so use at your own risk...

Resources