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

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.

Related

Recursive Text Substitution and File Extension Rename

I am using an application that creates a text file on a Linux server. I then have the ability to execute a shell script (BASH 3.2.57) in which I need to convert the text file from Unix line endings to DOS and also change the extension of the file from .txt to .log.
I currently have a sed based command to do this. This command is rewritten by the application at run time to point to the specific folder and file name, in this example where you see ABC (all capital 3 letters in all my examples are a variable that can be any 3 letters).
pushd /rootfolder/parentfolder/ABC/
sed 's/$/\r/' prABC.txt > prABC.log
popd
The problem with this is that if a user runs the application for 2 different groups, say ABC and DEF at nearly the same time, the script will get overwritten with the DEF variables before ABC had a chance to fire off and do its thing with the file. Additionally the .txt is left in the folder regardless and I would like that to be removed.
A friend of mine came up with the following code that seems to work if its determined to be our best solution, but I would think and hope we have a cleaner more dynamic way to do this. Also this current method requires that when my user decides to add a GHI directory and file I now have to update the code, which i can program my application to do for me but i don't want this script to have to be rewritten every time the application wants to use it.
pushd /rootfolder/parentfolder/ABC
if [[ -f prABC.txt ]]
then
sed 's/$/\r/' prABC.txt > prABC.log
rm prABC.txt
fi
popd
pushd /rootfolder/parentfolder/DEF
if [[ -f prABC.txt ]]
then
sed 's/$/\r/' prABC.txt > prABC.log
rm prABC.txt
fi
popd
I would like to call this script at anytime from my application and it find any file named pr*.txt below the /rootfolder/parentfolder/ directory (if that has to include the parentfolder in its search that won't be a problem) and convert the line endings from LF to CRLF and change the extension of the file from .txt to .log.
I've done a ton of searching and have found near solutions for this but not exactly what I need and I want to be sure it's as safe as possible (issues with using "find with for". I don't know what utilities are installed on this build so i would like to keep it as basic/supportable as possible Thanks in advance :)
You should almost never need pushd and popd in scripts. In fact, you rarely need cd, either.
#!/bin/bash
for d in /rootfolder/parentfolder/ABC /rootfolder/parentfolder/DEF
do
if [[ -f "$d/prABC.txt" ]]
then
sed 's/$/\r/' "$d/prABC.txt" > "$d/prABC.log" &&
rm "$d/prABC.txt"
fi
done
Recall that a && b is shorthand for
if a; then
b
fi
In other words, if sed fails (because the source file can't be read, or the destination can't be written) we don't rm the source file. There should be an error message already so we don't add another one.
Not only is this more succinct, it is also easier to change if you decide that the old file should be renamed instead of removed, or you want to filter out all lines which contain "beef" in the sed script. Generally you should avoid repeated code; see also the DRY principle on Wikipedia.
Something is seriously wrong somewhere if you require DOS line endings in your files on Unix.

How to configure vim to save extra copies

Is there a way to configure vim so that, instead of creating a temporary .swp, every time a save is made, suppose that I'm editing the file name_of_the_file.txt, the program automatically creates a file containing the previous save, named with the data and time of the saving, e.g. name_of_the_file-05-17-2017-11:20.txt in a folder, let say ~/.vim-bckp/name_of_the_file/? Or, better, having a custom command for saving that do the above request and avoid flooding the HDD with minor changes.
This script will do the trick. You could name it vimBackup or anything, do not use existing commands name. Then you could copy the command somewhere into the paths in $PATH variable or append $PATH variable with a custom script folder containing this script. Then you able to use it without using the full path to execute it.
#!/bin/sh
[[ $1 == "" ]] && echo "Command expect a file path" && exit 1
[[ ! $# == 1 ]] && echo "Command expect only one parameter" && exit 1
[[ -w $1 ]] && cp $1 $1_$(date +%Y-%m-%d_%H:%M) # if exist and writable make a copy
vim $1
I've written the writebackup plugin for that. That turned into a full plugin suite:
The writebackupVersionControl plugin plugin complements this script with additional commands and enhances the :WriteBackup command with more checks, but is not required.
The writebackupToAdjacentDir plugin implements a WriteBackup-dynamic-backupdir configuration that puts the backup files in an adjacent backup directory if one exists. This helps where the backups cannot be placed into the same directory.
The writebackupAutomator plugin automatically writes a backup on a day's first write of a file that was backed up in the past, but not yet today. It can be your safety net when you forget to make a backup.
So, once I have made a backup, I don't need to think about explicitly triggering one. I can view diffs, and even restore previous versions. Of course, if you can use a "full" version control system (like Git or Subversion), use that. But this one is nice for all the little configuration changed distributed throughout the file system.

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

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

Need to monitor directory change, and perform action

1st of all: I'm not programmer, neither Linux guru, just have to work with Linux, Oracle, shell scripts.
My current task is to monitor a table in Oracle (tool: sqlplus), and if it contains a certain row, then watch a linux directory for a growing tmp file, and log its attributes (e.g. ls -l), in every 5 second.
The most important part is: this tmp file will be deleted if the above record is deleted from the oracle table, and I need the last contents of this tmp file.
I can't control the Oracle data, just got query rights.
The available tools are: bash, awk, sed, some old version of perl, ruby (not 1.9*), and python (2.5). I don't have install rights, so most of the outside libraries are not accessible. I know I can run some libraries from my $HOME, but I don't have internet connection on that machine: so can't download any library.
Inotify is not available (older kernel).
Any idea where to start/how to do it? Thanks in advance.
How about creating a hard link in another directory, then, when the file "disappears" in the original location, the hard link will still have access to the content.
This is ugly and naive... but...
#!/bin/bash
WASTHERE=0
MONITORING=/tmp/whatever.dat
LASTBACKUP=/tmp/mybackup.dat
LOGFILE=/tmp/mylog.log
# Just create an empty file to start with
touch "$LASTBACKUP"
while [ 1 ];
do
if [[ ! -e "$MONITORING" ]]; then
if [[ $WASTHERE -ne 0 ]]; then
echo "File is gone! Do something with $LASTBACKUP";
WASTHERE=0
fi
else
WASTHERE=1
ls -l "$MONITORING" >> $LOGFILE
cp "$MONITORING" "$LASTBACKUP"
fi
sleep 5
done
The unfortunate part about this is that if anything happens to the file being 'monitored' while the script is sleeping (content is written to it, for example) and the file is then deleted before the script wakes up, the newly written content will not be in the 'backup.'

How can a bash script know the directory it is installed in when it is sourced with . operator?

What I'd like to do is to include settings from a file into my current interactive bash shell like this:
$ . /path/to/some/dir/.settings
The problem is that the .settings script also needs to use the "." operator to include other files like this:
. .extra_settings
How do I reference the relative path for .extra_settings in the .settings file? These two files are always stored in the same directory, but the path to this directory will be different depending on where these files were installed.
The operator always knows the /path/to/some/dir/ as shown above. How can the .settings file know the directory where it is installed? I would rather not have an install process that records the name of the installed directory.
I believe $(dirname "$BASH_SOURCE") will do what you want, as long as the file you are sourcing is not a symlink.
If the file you are sourcing may be a symlink, you can do something like the following to get the true directory:
PRG="$BASH_SOURCE"
progname=`basename "$BASH_SOURCE"`
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
dir=$(dirname "$PRG")
Here is what might be an elegant solution:
script_path="${BASH_SOURCE[0]}"
script_dir="$(cd "$(dirname "${script_path}")" && pwd)"
This will not, however, work when sourcing links. In that case, one might do
script_path="$(readlink -f "$(readlink "${BASH_SOURCE[0]}")")"
script_dir="$(cd "$(dirname "${script_path}")" && pwd)"
Things to note:
arrays like ${array[x]} are not POSIX compliant - but then, the BASH_SOURCE array is only available in Bash, anyway
on macOS, the native BSD readlink does not support -f, so you might have to install GNU readlink using e.g. brew by brew install coreutils and replace readlink by greadlink
depending on your use case, you might want to use the -e or -m switches instead of -f plus possibly -n; see readlink man page for details
A different take on the problem - if you're using "." in order to set environment variables, another standard way to do this is to have your script echo variable setting commands, e.g.:
# settings.sh
echo export CLASSPATH=${CLASSPATH}:/foo/bar
then eval the output:
eval $(/path/to/settings.sh)
That's how packages like modules work. This way also makes it easy to support shells derived from sh (X=...; export X) and csh (setenv X ...)
We found $(dirname "$(realpath "$0")") to be the most reliable with both sh and bash. As team mates used them interchangeably, we ran into problems with $BASH_SOURCE which is not supported by sh.
Instead, we now rely on dirname, which can also be stacked to get parent, or grandparent folders.
The following example returns the parent dir of the folder that contains the .sh file:
parent_path=$(dirname "$(dirname "$(realpath "$0")")")
echo $parent_path
I tried messing with variants of $(dirname $0) but it fails when the .settings file is included with ".". If I were executing the .settings file instead of including it, this solution would work. Instead, the $(dirname $0) always returns ".", meaning current directory. This fails when doing something like this:
$ cd /
$ . /some/path/.settings
This sort of works. It works in the sense that you can use the $(dirname $0) syntax within the .settings file to determine its home since you are executing this script in a new shell. However, it adds an extra layer of convolution where you need to change lines such as:
export MYDATE=$(date)
to
echo "export MYDATE=\$(date)"
Maybe this is the only way?

Resources