File and directory with same name in same parent directory - Solaris 8, ufs - file

Ok, I have been working with Solaris for a 10+ years, and have never seen this...
I have a directory listing which includes both a file and subdirectory with the same name:
-rw-r--r-- 1 root other 15922214 Nov 29 2006 msheehan
drwxrwxrwx 12 msheehan sysadmin 2048 Mar 25 15:39 msheehan
I use file to discover contents of the file, and I get:
bash-2.03# file msheehan
msheehan: directory
bash-2.03# file msh*
msheehan: ascii text
msheehan: directory
I am not worried about the file, but I want to keep the directory, so I try rm:
bash-2.03# rm msheehan
rm: msheehan is a directory
So here is my two part question:
What's up with this?
How do I carefully delete the file?
Jonathan
Edit:
Thanks for the answers guys, both (so far) were helpful, but piping the listing to an editor did the trick, ala:
bash-2.03# ls -l > jb.txt
bash-2.03# vi jb.txt
Which contained:
-rw-r--r-- 1 root other 15922214 Nov 29 2006 msheehab^?n
drwxrwxrwx 12 msheehan sysadmin 2048 Mar 25 15:39 msheehan
Always be careful with the backspace key!

I would guess that these are in fact two different filenames that "look" the same, as the command file was able to distinguish them when the shell passed the expanded versions of the name in. Try piping ls into od or another hex/octal dump utility to see if they really have the same name, or if there are non-printing characters involved.

I'm wondering what could cause this. Aside from filesystem bugs, it could be caused by a non-ascii chararacter that got through somehow. In that case, use another language with easier string semantics to do the operation.
It would be interesting to see what would be the output of this ruby snippet:
ruby -e 'puts Dir["msheehan*"].inspect'

You can delete using the iNode
If you use the "-i" option in "ls"
$ ls -li
total 1
20801 -rw-r--r-- 1 root root 0 2010-11-08 01:55 a?
20802 -rw-r--r-- 1 root root 0 2010-11-08 01:55 a\?
$ find . -inum 20802 -exec rm {} \;
$ ls -li
total 1
20801 -rw-r--r-- 1 root root 0 2010-11-08 01:55 a?
I've an example (in Spanish) how you can delete a file using then iNode on Solaris
http://sparcki.blogspot.com/2010/03/como-eliminar-archivos-utilizando-su.html
Urko,

And a quick answer to part 2 of my own question...
I would imagine I could rename the directory, delete the file, and rename the directory back to it's original again.
... I would still be interested to see what other people come up with.
JB

I suspect that one of them has a strange character in the name. You could try using the shell wildcard expansion to see that: type
cat msh*
and press the wildcard expansion key (in my shell it's Ctrl-X *). You should get two names listed, perhaps one of which has an escape character in it.

To see if there are special characters in your file, Try the -b or -q options to ls,
assuming solaris 8 has those options.
As another solution to deleting the file you can bring up the graphical file browser
(gasp!) and drag and drop the unwanted file to the trash.
Another solution might be to move the one file to a different name (the one without the unknown special character), then delete the special character directory name with wildcards.
mv msheehan temp
rm mshee*
mv temp msheehan
Of course, you want to be sure that only the file you want to delete matches the wildcard.
And, for your particular case, since one was a directory and the other a file, this command might have solved it all:
rmdir msheeha*

One quick-and-easy way to see non-printing characters and whitespace is to pipe the output through cat -vet, e.g.:
# ls -l | cat -vet
Nice and easy to remember!

For part 2, since one name contains two extra characters, you can use:
mv sheehan abc
mv sheeha??n xyz
Once you've done that, you've got sane file names again, that you can fix up as you need.

Related

How do I create a crontab job in unix that will move all files in my home directory to another directory at a specific time/date?

Hello I'm new to Unix and I am trying to create a crontab job that moves all the files I have in my home directory where the name contains the letter f followed by a digit 1,3 or 7 to a directory called backups, on the 12th of April and November at 9:30 PM.
This is my home directory:
arsenal.by flhome list1 stmnpgs
arsenal.pass flhome2 list2 test.c
assignment foreachScript1 list2.c testdir
availisting.csv funxdir local.cshrc testfile
backups funxdir2 local.login tmp.test
backups1 homlnk local.profile train
biglist lab4 myfile treat
biglist.c lab5 myfile2 trick
biglist2 lab6 Myhome.list tricking
CFiles.tar.Z lab7 myinfo.fl troll
clssnotes.txt lab8 myList typescript
delfh lec3 names.txt workdir
If anyone could help me out with this it'd be much appreciated!
Firstly home rolling a backup solution for work, professional or college, is usually a bad idea because the stakes of an error are potentially very high and local backups obviously have the possibility of being lost by whatever causes the original files to be inaccessible.
However it's a worthwhile exercise to show how you would do it in cron as it's a frequent type of task and it would provide you some cover while looking for a better solution.
Your date specification can be safely done as one cron entry as only the day of the year varies, if both the minute of the day and the day of the year (or the day of week) changed you would need two entries.
# M H DoM MoY DoW
30 21 12 4,10 * BACKUPDIR=~/backups; ds=$(date +\%Y\%m\%d\%H\%M\%S); mkdir -p $BACKUPDIR; find ~/* -type d -prune -o -type f -name f\*\[137\] -exec mv {} $BACKUPDIR/{}.$ds \;
The find command is told to look at all entries in your home directory that do not start with a . ("visible" files), if they are directories to ignore them (do not descend the directory tree) and if they are files that start with an f to move them (not copy them) to the $BACKUPDIR. If you wanted any file containing an f instead the find pattern would be \*f\*\[137\]
Above we define two variables for the backup dir and a datestamp (the \ before the % are because it is is a special character to cron).
The file globbing patterns * and [] are similarly escaped because they are shell special and we want to pass them to the find command.
The reason to use a timestamp is that moving or copying files frequently causes unintentional overwriting of files so if the backup directory path does not contain a date stamp then the target file name should.
Lastly it might be better to use a tar command to create a compressed date stamped archive that you can easily copy elsewhere, a local backup directory is asking for trouble, particularly if nested underneath the directory you are working in.
eg: Something like
#!/bin/bash
backup_file=~/backups/backup.$(date +%Y%m%d%H%M%S).tar.gz
tar czf $backup_file $(find ~/* -type d -prune -o -type f -name f\*\[137\] -print)
# <Commands to copy the file elsewhere here>
# You should then copy this file elsewhere (another system) or email it to yourself (after possibly encrypting it)

Using "-" to start files and not conflict with flags

I am trying to replicate the "ls" command in C. It should take anywhere between 0 and 2 arguments, those being a file path and a set of flags. When 1 argument is passed, I am trying to distinguish between passing a file and passing a set of flags - I would have thought the obvious way to go about it was to assume no file names begin with a "-" character and so therefore, if the first character of the argument is a "-" then treat it as a set of flags, otherwise treat it as a file path.
How should I actually distinguish between the two?
well, the rule with ls is that — considering -a is a file:
ls -a : -a is considered as an option argument ;
ls -- -a : -a is considered as a file argument
the -- argument is considered as a separator, after which all arguments are files, not options.
Typically, programs don't, and leave it to the user to deal with the resulting problems.
For example, create a file called -l, and at least one other file, and then run ls *:
me#localhost:~$ mkdir temp
me#localhost:~$ cd temp
me#localhost:~/temp$ touch ./-l
me#localhost:~/temp$ touch testfile
me#localhost:~/temp$ ls *
-rw-rw-r-- 1 acampbell acampbell 0 Apr 4 11:00 testfile
me#localhost:~/temp$
ls * expanded to ls -l testfile.
Most Unix utilities can take the argument --, and every argument after -- will be treated as a filename:
me#localhost:~/temp$ ls -l -- testfile
-rw-rw-r-- 1 acampbell acampbell 0 Apr 4 11:00 testfile
me#localhost:~/temp$ ls -- -l testfile
-l testfile
me#localhost:~/temp$
They can also specify a path that doesn't start with -, such as by using a redundant ./:
me#localhost:~/temp$ ls ./*
./-l ./testfile
me#localhost:~/temp$

How to check which files are being ignored because of .hgignore?

I'm quite often concerned that my hgignore file may be excluding important files. For example I just noticed that I was excluding all .exe files which excluded some little executable tools which should be kept with the source. It was a simple change to include them but makes me worried that the rules could have un-intended consequences.
Is there a way to view a list of all the files which are not being tracked due to the .hgignore file? Just so I can periodically review the list to check I'm happy with it.
The command hg status -i does exactly that.
#Jon beat me to the punch with the right answer, but its worth nothing that along with status -i, there is:
hg status -m (only modified files)
hg status -a (only files that were added)
hg status -r (only files that were removed)
hg status -d (only files that were deleted)
hg status -u (all non-tracked files)
hg status -c (files with no changes, ie. "clean")
hg status -A (all files, ie, everything)
If you want to do manual inspection on the file names, then use the -i/--ignored flag to status:
$ hg status -i
I ignored file.exe
If you want the file names alone, then use -n/--no-status to suppress the I status code printed in front of each filename:
$ hg status -n -i
ignored file.exe
If you need to process the files with xargs, then use the -0/--print0 flag in addition:
$ hg status -n -0 | xargs -0 touch
That will take care of handling spaces correctly — with using -0, there is a risk that you'll end up treating ignored file.exe as two files: ignored and file.exe since shells normally split on spaces.
The above commands show you untracked files matching .hgignore. If you want to solve the related problem of finding tracked files matching .hgignore, then you need to use a fileset query. That looks like this:
$ hg locate "set:hgignore()"
You can use filesets with all commands that operate on files, so you can for example do:
$ hg forget "set:hgignore()"
to schedule the files found for removal (with a copy left behind in your working copy).
Yes, it is Possible.
If You're using smth like TortoiseHg, You can select what files You wanna see.
Here's a sample

Moving things in terminal based on their name

Edit: I think this has been answered successfully, but I can't check 'til later. I've reformatted it as suggested though.
The question: I have a series of files, each with a name of the form XXXXNAME, where XXXX is some number. I want to move them all to separate folders called XXXX and have them called NAME. I can do this manually, but I was hoping that by naming them XXXXNAME there'd be some way I could tell Terminal (I think that's the right name, but not really sure) to move them there. Something like
mv *NAME */NAME
but where it takes whatever * was in the first case and regurgitates it to the path.
This is on some form of Linux, with a bash shell.
In the real life case, the files are 0000GNUmakefile, with sequential numbering. I'm having to make lots of similar-but-slightly-altered versions of a program to compile and run on a cluster as part of my research. It would probably have been quicker to write a program to edit all the files and put in the right place in the first place, but I didn't.
This is probably extremely simple, and I should be able to find an answer myself, if I knew the right words. Thing is, I have no formal training in programming, so I don't know what to call things to search for them. So hopefully this will result in me getting an answer, and maybe knowing how to find out the answer for similar things myself next time. With the basic programming I've picked up, I'm sure I could write a program to do this for me, but I'm hoping there's a simple way to do it just using functionality already in Terminal. I probably shouldn't be allowed to play with these things.
Thanks for any help! I can actually program in C and Python a fair amount, but that's through trial and error largely, and I still don't know what I can do and can't do in Terminal.
SO many ways to achieve this.
I find that the old standbys sed and awk are often the most powerful.
ls | sed -rne 's:^([0-9]{4})(NAME)$:mv -iv & \1/\2:p'
If you're satisfied that the commands look right, pipe the command line through a shell:
ls | sed -rne 's:^([0-9]{4})(NAME)$:mv -iv & \1/\2:p' | sh
I put NAME in brackets and used \2 so that if it varies more than your example indicates, you can come up with a regular expression to handle your filenames better.
To do the same thing in gawk (GNU awk, the variant found in most GNU/Linux distros):
ls | gawk '/^[0-9]{4}NAME$/ {printf("mv -iv %s %s/%s\n", $1, substr($0,0,4), substr($0,5))}'
As with the first sample, this produces commands which, if they make sense to you, can be piped through a shell by appending | sh to the end of the line.
Note that with all these mv commands, I've added the -i and -v options. This is for your protection. Read the man page for mv (by typing man mv in your Linux terminal) to see if you should be comfortable leaving them out.
Also, I'm assuming with these lines that all your directories already exist. You didn't mention if they do. If they don't, here's a one-liner to create the directories.
ls | sed -rne 's:^([0-9]{4})(NAME)$:mkdir -p \1:p' | sort -u
As with the others, append | sh to run the commands.
I should mention that it is generally recommended to use constructs like for (in Tim's answer) or find instead of parsing the output of ls. That said, when your filename format is as simple as /[0-9]{4}word/, I find the quick sed one-liner to be the way to go.
Lastly, if by NAME you actually mean "any string of characters" rather than the literal string "NAME", then in all my examples above, replace NAME with .*.
The following script will do this for you. Copy the script into a file on the remote machine (we'll call it sortfiles.sh).
#!/bin/bash
# Get all files in current directory having names XXXXsomename, where X is an integer
files=$(find . -name '[0-9][0-9][0-9][0-9]*')
# Build a list of the XXXX patterns found in the list of files
dirs=
for name in ${files}; do
dirs="${dirs} $(echo ${name} | cut -c 3-6)"
done
# Remove redundant entries from the list of XXXX patterns
dirs=$(echo ${dirs} | uniq)
# Create any XXXX directories that are not already present
for name in ${dirs}; do
if [[ ! -d ${name} ]]; then
mkdir ${name}
fi
done
# Move each of the XXXXsomename files to the appropriate directory
for name in ${files}; do
mv ${name} $(echo ${name} | cut -c 3-6)
done
# Return from script with normal status
exit 0
From the command line, do chmod +x sortfiles.sh
Execute the script with ./sortfiles.sh
Just open the Terminal application, cd into the directory that contains the files you want moved/renamed, and copy and paste these commands into the command line.
for file in [0-9][0-9][0-9][0-9]*; do
dirName="${file%%*([^0-9])}"
mkdir -p "$dirName"
mv "$file" "$dirName/${file##*([0-9])}"
done
This assumes all the files that you want to rename and move are in the same directory. The file globbing also assumes that there are at least four digits at the start of the filename. If there are more than four numbers, it will still be caught, but not if there are less than four. If there are less than four, take off the appropriate number of [0-9]s from the first line.
It does not handle the case where "NAME" (i.e. the name of the new file you want) starts with a number.
See this site for more information about string manipulation in bash.

Change File Encoding to utf-8 via vim in a script

I just got knocked down after our server has been updated from Debian 4 to 5.
We switched to UTF-8 environment and now we have problems getting the text printed correctly on the browser, because all files are in non-utf8 encodings like iso-8859-1, ascii, etc.
I tried many different scripts.
The first one I tried is "iconv". That one doesn't work, it changes the content, but the file's encoding is still non-utf8.
Same problem with enca, encamv, convmv and some other tools I installed via apt-get.
Then I found a python code, which uses chardet Universal Detector module, to detect encoding of a file (which works fine), but using the unicode class or the codec class to save it as utf-8 doesn't work, without any errors.
The only way I found to get the file and its content converted to UTF-8, is vi.
These are the steps I do for one file:
vi filename.php
:set bomb
:set fileencoding=utf-8
:wq
That's it. That one works perfect. But how can I get this running via a script?
I would like to write a script (Linux shell) which traverses a directory taking all php files, then converting them using vi with the commands above.
As I need to start the vi app, I do not know how to do something like this:
"vi --run-command=':set bomb, :set fileencoding=utf-8' filename.php"
Hope someone can help me.
This is the simplest way I know of to do this easily from the command line:
vim +"argdo se bomb | se fileencoding=utf-8 | w" $(find . -type f -name *.php)
Or better yet if the number of files is expected to be pretty large:
find . -type f -name *.php | xargs vim +"argdo se bomb | se fileencoding=utf-8 | w"
You could put your commands in a file, let's call it script.vim:
set bomb
set fileencoding=utf-8
wq
Then you invoke Vim with the -S (source) option to execute the script on the file you wish to fix. To do this on a bunch of files you could do
find . -type f -name "*.php" -exec vim -S script.vim {} \;
You could also put the Vim commands on the command line using the + option, but I think it may be more readable like this.
Note: I have not tested this.
You may actually want set nobomb (BOM = byte order mark), especially in the [not windows] world.
e.g., I had a script that didn't work as there was a byte order mark at the start. It isn't usually displayed in editors (even with set list in vi), or on the console, so its difficult to spot.
The file looked like this
#!/usr/bin/perl
...
But trying to run it, I get
./filename
./filename: line 1: #!/usr/bin/perl: No such file or directory
Not displayed, but at the start of the file, is the 3 byte BOM. So, as far as linux is concerned, the file doesn't start with #!
The solution is
vi filename
:set nobomb
:set fileencoding=utf-8
:wq
This removes the BOM at the start of the file, making it correct utf8.
NB Windows uses the BOM to identify a text file as being utf8, rather than ANSI. Linux (and the official spec) doesn't.
The accepted answer will keep the last file open in Vim. This problem can be easily resolved using the -c option of Vim,
vim +"argdo set bomb | set fileencoding=utf-8 | w" -c ":q" file1.txt file2.txt
If you need only process one file, the following will also work,
vim -c ':set bomb' -c ':set fileencoding=utf-8' -c ':wq' file1.txt

Resources