Why do some linux commands work with a "*" in a list of files but others do not? - linux

why does, for instance,
ls -1 /path/to/something/data*data/file*.txt
work fine, while something like the following returns an error:
tar -xzvf *tar.gz
tar: evsClient-v.0.0.6.tar.gz: Not found in archive
tar: evsClient-v.0.0.7.tar.gz: Not found in archive

The -f option to tar only expects one argument to specify the file to process. If using a glob expression as you have to tar -xzvf and there are multiple files that get expanded as a result, the files after the first one are taken to be regular arguments to tar, not an option argument to -f.
Since you are using -x, tar is in extraction mode, and it is taking the other files to be the name of files to be extracted from the archive that it is operating on.

When * isn't quoted, any word containing it is treated as a shell pattern, which expands to a list of file names matching that pattern.
In your first example, the pattern expands to a list of existing files, which ls then dutifully displays.
In your second example, the pattern again expands to a list of matching files. However, only the first member of that list is treated as the argument to the f option. The remaining items are names of files you want to extract from the first one, which is not what you intended.
The general rule is that the pattern simply provides a list of file names; it's up to you to ensure that the resulting list of files is a correct set of arguments for the command you are running.

The "*" is actually expanded by the shell and the resulting list of filenames are then presented as arguments to the command in question.
The "ls" command supports a list of file names and so does the "tar" command. But the signature of tar is:
tar option(s) archive_name file_name(s)
So - in your example I assume that the command line is expanded to:
tar -xzvf evsClient-v.0.0.5.tar.gz evsClient-v.0.0.6.tar.gz evsClient-v.0.0.7.tar.gz
giving you the error because the two latter archives cannot be extracted from the first.

Related

Zip files within the directory without file extensions

I'm trying to zip all the files within a directory which contains .py files individually. But after zipping the files the output that I'm seeing is .py.zip vs just .zip
Here's the one liner command that I'm trying to execute.
cd scripts/python/
for i in *; do zip $i.zip $i; done
This is what you are looking for:
for i in *py; do
zip "${i%.*}".zip "$i";
done
Explanation
${i%.*}: This makes use of Bash's built in parameter expansion. Here it tries to match everything after %. If it does find a match, it uses everything before the match. https://www.gnu.org/software/bash/manual/bash.html#Shell-Parameter-Expansion for more information.

How to exclude multiple directories with Exuberant ctags?

I have looked and tried to use exuberant ctags with no luck with what I want to do. I am on a Mac trying to work in a project where I want to exclude such directories as .git, node_modules, test, etc. When I try something like ctags -R --exclude=[.git, node_modules, test] I get nothing in return. I really only need to have it run in my core directory. Any ideas on how to accomplish this?
The --exclude option does not expect a list of files. According to ctags's man page, "This option may be specified as many times as desired." So, it's like this:
ctags -R --exclude=.git --exclude=node_modules --exclude=test
Read The Fantastic Manual should always be the first step of any attempt to solve a problem.
From $ man ctags:
--exclude=[pattern]
Add pattern to a list of excluded files and directories. This option may
be specified as many times as desired. For each file name considered by
both the complete path (e.g. some/path/base.ext) and the base name (e.g.
base.ext) of the file, thus allowing patterns which match a given file
name irrespective of its path, or match only a specific path. If appro-
priate support is available from the runtime library of your C compiler,
then pattern may contain the usual shell wildcards (not regular expres-
sions) common on Unix (be sure to quote the option parameter to protect
the wildcards from being expanded by the shell before being passed to
ctags; also be aware that wildcards can match the slash character, '/').
You can determine if shell wildcards are available on your platform by
examining the output of the --version option, which will include "+wild-
cards" in the compiled feature list; otherwise, pattern is matched
against file names using a simple textual comparison.
If pattern begins with the character '#', then the rest of the string is
interpreted as a file name from which to read exclusion patterns, one per
line. If pattern is empty, the list of excluded patterns is cleared.
Note that at program startup, the default exclude list contains "EIFGEN",
"SCCS", "RCS", and "CVS", which are names of directories for which it is
generally not desirable to descend while processing the --recurse option.
From the two first sentences you get:
$ ctags -R --exclude=dir1 --exclude=dir2 --exclude=dir3 .
which may be a bit verbose but that's what aliases and mappings and so on are for. As an alternative, you get this from the second paragraph:
$ ctags -R --exclude=#.ctagsignore .
with the following in .ctagsignore:
dir1
dir2
dir3
which works out to excluding those 3 directories without as much typing.
You can encapsulate a comma separated list with curly braces to handle multiples with one --exclude option:
ctags -R --exclude={folder1,folder2,folder3}
This appears to only work for folders in the root of where you're issuing the command. Excluding nested folders requires a separate --exclude option.
The other answers were straight to the point, and I thought a little example may help:
You should add an asterisk unix-like style to exclude the whole directory.
ctags -R --exclude={.git/*,.env/*,.idea/*} ./
A bit late but following on romainl response, you could use your .gitignore file as a basis, you only need to remove any leading slashes from the file, like so:
sed "s/\///" .gitignore > .ctagsignore
ctags -R --exclude=#.ctagsignore
I really only need to have it run in my core directory.
Simply remove the -R (recursion) flag!!!

tar files using the -C option and wildcard

I'm passing a tar command to shell executor in an application. But it seems that my tar syntax is incorrect. (This is Windows (bsdtar command) but works the same as Linux as far as I know; I can also test on Linux if need be.)
I'm trying to tar gz everything up all files ending in ext without storing the full path in my tar file.
tar -cvzf test.tar.gz -C C:/mydir/toTar/ *.ext
I get an error:
tar: *.ext: Cannot stat: No such file or directory
I can give the whole path but then my tar will contain C->mydir->toTar->. I just want the files, not mydir and toTar in the result.
So far only thing that is close to what I want is . instead of *.ext, but that tars other things too, which I obviously don't want.
The problem is that * is a wildcard character that is expanded by the shell, but you are bypassing the shell and calling tar directly. The tar command is looking for one file which is named literally *.ext and it does not exist.
Your options are:
Expand the list of files in your own code and pass that list to tar.
Call the shell from your code by calling something like /bin/sh -c tar ...
With option 2 there may be security implications -- if the shell sees something it thinks is a command, it will run it. Option 1 is therefore safer, but it's up to you which makes more sense.
I am befuddled by how you're using dos-style paths in an apparently linux-like context. But this is how I'd do it. Hopefully the concept is clear if the details may be incorrect.
cd C:/mydir/toTar/
mkdir ~/tmpwork
find . -name '*.ext' > ~/tmpwork/extfiles
tar czvfT ~/tmpwork/test.tar.gz ~/tmpwork/extfiles
rm ~/tmpfiles/extfiles
There is no way around the shell expansion without using pipes, etc.

UNIX Shell: List all files in directory excluding directory names

I want to list all the files in a directory recursively. I am storing this output in a file that I will later iterate through and use each line as an argument in another command.
However all the commands I have tried have listed the directory name as one of the output lines followed by the files and directories contained in the directory.
I have tried the following:
tree -if --noreport . > files_names.txt
This has given me some success, but it still prints the directories. An example output is as follows:
/testdir
/testdir/rightfolder/
/testdir/rightfolder/file2.txt
/testdir/rightfolder/file3.txt
/testdir/wrongfolder/
/testdir/wrongfolder/file.txt
I have checked the man pages for tree and ls.
Is there a flag or another command that will give me the correct output. I have considered using a flag for tree to list the directories and then removing all those entries from the original list but this is not elegant at all.
You could use find(1) and filter by type:
find ./ -type f

Getting contents of a particular file in the tar archive

This script lists the name of the file ( in a tar archive) containing a pattern.
tar tf myarchive.tar | while read -r FILE
do
if tar xf test.tar $FILE -O | grep "pattern" ;then
echo "found pattern in : $FILE"
fi
done
My question is:
Where is this feature documented, where $FILE is one of the files in the archive:
tar xf test.tar $FILE
This is usually documented in man pages, try running this command:
man tar
Unfortunately, Linux has not the best set of man pages. There is an online copy of tar manpage from this OS: http://linux.die.net/man/1/tar and it is terrible. But it links to info man command which is command to access the "info" system widely used in GNU world (many programs in linux user-space are from GNU projects, for example gcc). There is an exact link to section of online info tar about extracting specific files: http://www.gnu.org/software/tar/manual/html_node/extracting-files.html#SEC27
I may also recommend documentation from BSD (e.g. FreeBSD) or opengroup.org. Utilities can be different in detail but behave same in general.
For example, there is some rather old but good man from opengroup (XCU means 'Commands and Utilities' of the Single UNIX Specification, Version 2, 1997):
http://pubs.opengroup.org/onlinepubs/7908799/xcu/tar.html
tar key [file...]
The following operands are supported:
key --
The key operand consists of a function letter followed immediately by zero or more modifying letters. The function letter is one of the following:
x --
Extract the named file or files from the archive. If a named file matches a directory whose contents had been written onto the archive, this directory is (recursively) extracted. If a named file in the archive does not exist on the system, the file is created with the same mode as the one in the archive, except that the set-user-ID and set-group-ID modes are not set unless the user has appropriate privileges. If the files exist, their modes are not changed except as described above. The owner, group, and modification time are restored (if possible). If no file operand is given, the entire content of the archive is extracted. Note that if several files with the same name are in the archive, the last one overwrites all earlier ones.
And to fully understand command tar xf test.tar $FILE you should also read about f option:
f --
Use the first file operand (or the second, if b has already been specified) as the name of the archive instead of the system-dependent default.
So, test.tar in your command will be used by f key as archive name; then x will use second argument ($FILE) as name of file or directory to extract from archive.

Resources