Whole document needs improvements !!!
Tfman is text file manager managing files via textual representation of file system (being ordinary text). It can scan directories to create textual representation or parse textual representation to retrieve actions specified by user.
. Written in C for Unix-like systems.
. Does not depend on external libraries.
. Can cooperate with command-line tools, like Sed, via standard input and output streams.
. Can be integrated with text editing tools, like Vim.
Tfman has two working modes: scan mode and parse mode.
The scan mode performs scanning on specified part of file system (directory or whole tree of directories) and composes textual representation of it. Result is send to standard output (this is default) or written to a file.
Created textual representation is formatted in a manner allowing direct use in parse mode. User can edit it as normal text and add action tags.
The parse mode retrieves actions specified in textual representation and performs them on file system. Textual representation is read from standard input (default) or from file. Multiple actions of same or different types can be specified on multiple entries, whole directories and groups.
The textual representation must be in specific format (but it is still ordinary text) and actions are named by tags.
Tfm is convenience tool that combines scan mode and parse mode to make Tfman use easier. It does scanning of directories, editing textual representation and parsing result in one step.
This section aims to be a quick introduction to the idea behind Tfman. Following examples present the way Tfman works and a few of its features. It does not aim to show the most effective way to use the program or to discuss all the features in detail.
The quickest way to make changes in a file system is probably Tfm tool, which allows direct jump into editing textual representation of given directory or whole directories tree. Issue this command to scan current directory resursively 3 levels deep and automatically open its textual representation for editing in program specified by 'EDITOR' shell variable:
tfm . -R=3
Now you can quickly find desired files with editor's search tools and add action tags. For example:
{ | [name] * DELETE ENTRY | [name2] ~ CREATE ENTRY | [name3] >>tar MOVE | [name4] >*tar OVERWRITE-COPY } user:group:rw-rw-rw- CHANGE OWNING USER, GROUP AND PERMISSIONS OF ALL GROUPPED ENTRIES ~ Dir/ CREATE DIRECTORY - NOTE TRAILING '/' AND SPACE IN FIRST COLUMN ~ Dir/file CREATE ENTRY
After you write changes and close editor, Tfman will parse resulting text, retrieve operations to perfmorm, show them all to you and ask for confirmation.
Tfman can be used from within Vim as multi-window file manager. Needed files - that is small plugin and syntax file - should have been installed to Vim's system directory during Tfman installation. If they haven't, copy them from /usr/share/tfman/vim/vimfiles/{plugin,syntax} to corresponding Vim directories.
Invoke Vim and issue following commands to use Tfmvi, text file manager inside Vim (remember you can map commands to keys):
:Tsr DIR
to open DIR in new or in existing tfman edit window. Edit textual representation adding desired actions, exactly like you usually edit text and parse it with:
:T
to perform operations and show results in tfman message window, opened for you below edit window. This time Tfman will not ask for confirmation like it did in command-line, so next time you may want to inspect operations first with:
:Tsh
it will show operations in message window without performing them.
:Tsw DIR 0
will scan DIR recursively (0 - all the way to bottom) and open result in new edit window. Now you have two edit windows and one message window for them.
ENTER
press 'ENTER' to scan entry under cursor and open result in current window.
-
press '-' to scan parent directory (go level up in hierarchy).
\m
(assuming your mapleader is '\') add move action tag in current line with entry name as target. This way you can quickly rename file.
\c
add copy action tag in current line
\r
add remove action tag in current line
Commands work in any window - if you are not currently in tfman edit window, first edit window in tabpage will be used or new window will be opened. Mappings work only in Tfman widnows.
See 'Vim integration' below and consult Tfman manpage for more information.
Create file with following content, let it be /tmp/t.tfm:
{ free_entry1 >/tmp /tmp/free_entry2 free_entry{7,8,9} :rwxrwxr-- free_entryA } ~ # Dir/ == ~ # Subdir # SubdirA | entry1 >>./entryA | entry2 >>entryB | ./entry3
Indentation is important! Default indentation step is two spaces. It means that every line must be indented by multiple of two. Indentation matters particularly for subdirectories, determining their place in directories hierarchy.
Change to safe directory and invoke Tfman:
$ tfman /tmp/t.tfm
Tfman will parse textual representation and perform operations specified in text. Operations are performed in fixed order, type by type. First all create operations, then copy, then remove etc. Operations of same type are performed in order of appearance in text.
What all those tags mean? First we wanted to create few files in current directory, begining their names with 'free_entry'. To achieve this we have grouped them using braces '{}' and specified '~' tag for whole group. We also used brace expansion for third entry to save typing and specify three names at once. Then we have decided to copy first entry to '/tmp' directory after it is created. We achieved exactly the same result for second entry, just created it in '/tmp' straightaway, eliminating copy step. We also changed permissions for three files in third entry from default to "rwxrwxr--".
In second part of file we wanted to create whole directory structure with subdirectories and files. Default top directory is current working directory (directory where Tfman was invoked) and it is assumed from begining of file. To replace it for succeding entries we had to put new top directory name 'Dir' before other entries. Everything after, until new top directory is specified, will be related to that directory. Then we have added subdirectories, carefully choosing indentation, because indentation determines to which directory above given subdirectory belongs. Then we have added few entries in one of subdirectories.
But how to create this whole structure? We need to specify create action for all entries belonging to 'Dir'. We have done that with '==' tag which tells Tfman to apply succeding actions to directory itself and to its content - that is all entries below it in text. That means that next create action '~' will create directory, subdirectories and all entries inside. Finally, we have decided to move two entries to different places using move tags '>>'. First entry goes to current working directory under name 'entryA', second entry is just renamed.
Now invoke Tfman as follows:
$ tfman Dir -R -C -F=name > /tmp/t2
to make recursive (-R) scan of 'Dir' with entry names cloning (-C) and writing result to '/tmp/t2' file, which should look like this:
# [Dir] Dir | [ ../] .. | [Subdir/] Subdir # [Dir/Subdir] Dir/Subdir | [ ../] .. | [SubdirA/] SubdirA # [Dir/Subdir/SubdirA] Dir/Subdir/SubdirA | [ ../] .. | [entry1] entry1 | [entry2] entry2 | [entry3] entry3 | [entry4] entry4 | [entry5] entry5 | [entry6] entry6
Edit file, find name 'entryB', add move action '>>' and modify target name, then save file. Last line should look like this:
| [entryB] >>entryX
Invoke Tfman to parse modified file:
$ tfman /tmp/t2
It will just rename 'entryB' to 'entryX'.
Now prepend this text to /tmp/t.tfm content:
* entry* * free_entry* * /tmp/{t.tfm,t2,free_entry{1,2}} * Dir END ... PREVIOUS FILE CONTENT ...
All created files are now removed and file system is in original state. We have used remove tag '*' to achieve this. 'END' tag terminates parsing immediately, preventing execution of actions in succeding lines.
Terms used above that may need some explanation:
You can pass textual representation to Tfman in several ways. For example, Tfman can read text from standard input. Just invoke Tfman, type text in and finish with ctrl-D:
$ tfman ~ tr NOTE BEGINING SPACE ^D
It will create empty file in current directory. '|' character is needed to make line active, '~' is action tag. First word that do not start with action tag is taken as name. Names can also be enclosed in '[]' brackets. That allows placing them anywhere in the line or begin name with character that would be otherwise recognized as action tag.
Another way is to send text to Tfman's standard input through pipe:
echo " tr *" | tfman
It will remove file tr.
You could also just create suitable text file by hand and then make Tfman to read it (you can use your favorite text editor instead of echo):
echo " tr" > f tfman f
It has no effect because there is no action defined, despite line is active.
All that is not very useful, nothing you couldn't do with standard console tools.
Better approach is to use Tfman scanning mode to create textual representation for specified part of file system and then edit it to add desired actions. Following example will scan current directory and write its textual representation to file 't':
tfman . > t
Textual representation is ready to edit and add actions to it. When you done, pass result to Tfman:
tfman t
Until now, using Tfman involves several steps: scaning, opening file, editing, saving file and quiting, then parsing and confirming operations. To avoid most of these steps make use of simple shell script installed together with Tfman. Invoke:
tfm .
It will scan current directory and open result in your favorite text editor, determined from EDITOR environment variable (currently 'vim' and 'nano' are tested to work, but other editros should work as well). If variable is not set or is empty, script defaults to 'vim'. After you save and quit editor, modified result is scanned and actions performed. There is vim syntax highlighting installed with Tfman that should make editing more pleasant.
Passing options to Tfman through Tfm:
tfm . -C -F=name,type,size
Reuse last textual representation (ie. to correct errors):
tfm
Help:
tfm -h
Default Tfman behavior is to utilize standard input and output streams, what allows to cimbine it with tools like Sed or Awk and probably others. I will give just one short example how it can be done.
This will move all directories in current directory tree with extension '.tmp' (together with content ofcourse):
$ tfman DIR -F=name,type -R | sed 's,\(^|.*.tmp/] d$\),\1 >>/tmp/common,' | tfman
Few simple examples of textual representation introducing action tags and names composition. All examples can be parsed by Tfman.
~ file1 >>target1 ~ file2 >target2
First both files are created in current working directory. Then 'file1' is moved to 'target1' and 'file2' is copied to 'target2'. If 'file1', 'file2' or any of targets exist Tfman will terminate, same for any other error.
# ~ file1 >>*target1 ~ file2 >*target2
Above representation works exactly the same way as previous one with very important exception: if any target exist it is removed. We also added directory line with empty name - it doesn't change anything. Empty name in directory line resets top directory to current working directory. We could achieve exactly the same result by specifing current directory explicitly:
# [.] ~ file1 >>*target1 ~ file2 >*target2
# Dir symlink1 ->target1 ./symlink2 ->./target2 /hlink =>/hlink
Two symbolic links and one hard link is created, but more interesting thing in above example is how entry and target names are composed.
First entry undergoes normal name composition. It means that directory in text above them is prepended to both name and action operand. Line virtually becomes:
Dir/symlink1 ->Dir/target1
Second entry has both name and target starting with './', which indicates that file name should be taken as relative to current directory (where Tfman was invoked), not to parent directory (placed in text). Line is not changed:
./symlink2 ->./target2
Same for third line where both name and target are absolute:
/hlink =>/hlink
To overwrite files if they already exist and replace them with newly created links use:
symlink ->*target1 hardlink =>*target2
If 'symlink' or 'hardlink' exists it will be removed prior to link creation.
Examples showing how Tfman can be used in more complex way.
Go to safe directory and parse following representation to create whole new structure of files:
{{ # Dir1 == ! mh:users:rwxrwxr-x | f1 :rwx------ # SubdirA | e1 # SubdirX | ex | ey # SubdirY ! am:users | ez # SubdirB :rwxr-x--- | e2 | e3 # Dir2 = :rwxrwxrwx | f2 | f3 }} ~
Here we have created desired files structure. Each line starts with line tag which defines line's type. If line doesn't begin with line tag it is not parsed at all. Line types are:
Both directory trees are gathered in one group. Create action is specified for this whole group. This way we create all files at once. We have used strong grouping ('{{') which groups directories instead of normal grouping ('{') which groups files only.
Following action tags are used in above textual representation to change properties of files. For detailed explanation how ':' tag works consult manpage.
We also used some special tags to change behavior of succeding actions:
As you can see actions can be specified for groups of entries at once. To affect whole directories use '=' or '==', to affect group of entries use '{{' (includes directories and files) or '{' (includes files only).
Now we have directories we can work with. First we need to make textual representation of it, then we will be able to edit it and add desired actions. Scan both directories recursively:
$ tfman Dir1 Dir2 -R > t
You will get this in file 't':
# [Dir1] D rwxrwxr-x mh users -- 4096 4096 m.2014-06-17 20:34:04 (none) | [ ../] d rwxr-sr-x 23 mh magazyn -- 4096 4096 m.2014-06-17 20:34:04 (none) | [Subdir/] d rwxr-sr-x 5 mh users -- 4096 4096 m.2014-06-17 20:34:04 (none) | [ f1] - rwx------ mh users -- 0 0 m.2014-06-17 20:34:04 (none) # [Dir1/Subdir] D rwxr-sr-x mh users -- 4096 4096 m.2014-06-17 20:34:04 (none) | [ ../] d rwxrwxr-x 4 mh users -- 4096 4096 m.2014-06-17 20:34:04 (none) | [SubdirA/] d rwxr-sr-x 5 mh users -- 4096 4096 m.2014-06-17 20:34:04 (none) | [SubdirB/] d rwxr-x--- 4 mh users -- 4096 4096 m.2014-06-17 20:34:04 (none) | [subdirC/] d rwxr-sr-x 2 mh users -- 4096 4096 m.2014-06-17 20:34:04 (none) # [Dir1/Subdir/SubdirA] D rwxr-sr-x mh users -- 4096 4096 m.2014-06-17 20:34:04 (none) | [ ../] d rwxr-sr-x 5 mh users -- 4096 4096 m.2014-06-17 20:34:04 (none) | [SubdirX/] d rwxr-sr-x 4 mh users -- 4096 4096 m.2014-06-17 20:34:04 (none) | [SubdirY/] d rwxr-sr-x 3 mh magazyn -- 4096 4096 m.2014-06-17 20:34:04 (none) | [ e1] - rw-r--r-- mh users -- 0 0 m.2014-06-17 20:34:04 (none) # [Dir1/Subdir/SubdirA/SubdirX] D rwxr-sr-x mh users -- 4096 4096 m.2014-06-17 20:34:04 (none) | [../] d rwxr-sr-x 5 mh users -- 4096 4096 m.2014-06-17 20:34:04 (none) | [ex] - rw-r--r-- mh users -- 0 0 m.2014-06-17 20:34:04 (none) | [ey] - rw-r--r-- mh users -- 0 0 m.2014-06-17 20:34:04 (none) # [Dir1/Subdir/SubdirA/SubdirY] D rwxr-sr-x mh magazyn -- 4096 4096 m.2014-06-17 20:34:04 (none) | [../] d rwxr-sr-x 5 mh users -- 4096 4096 m.2014-06-17 20:34:04 (none) | [ez] - rw-r--r-- mh users -- 0 0 m.2014-06-17 20:34:04 (none) # [Dir1/Subdir/SubdirB] D rwxr-x--- mh users -- 4096 4096 m.2014-06-17 20:34:04 (none) | [../] d rwxr-sr-x 5 mh users -- 4096 4096 m.2014-06-17 20:34:04 (none) | [e2] - rw-r--r-- mh users -- 0 0 m.2014-06-17 20:34:04 (none) | [e3] - rw-r--r-- mh users -- 0 0 m.2014-06-17 20:34:04 (none) # [Dir1/Subdir/subdirC] D rwxr-sr-x mh users -- 4096 4096 m.2014-06-17 20:34:04 (none) | [../] d rwxr-sr-x 5 mh users -- 4096 4096 m.2014-06-17 20:34:04 (none) # [Dir2] D rwxr-sr-x mh magazyn -- 4096 4096 m.2014-06-17 20:34:04 (none) | [../] d rwxr-sr-x 23 mh magazyn -- 4096 4096 m.2014-06-17 20:34:04 (none) | [f2] - rwxrwxrwx mh magazyn -- 0 0 m.2014-06-17 20:34:04 (none) | [f3] - rwxrwxrwx mh magazyn -- 0 0 m.2014-06-17 20:34:04 (none)
As you can see tfman wraps entry names with brackets. It allow names to be placed anywhere in the line, not only as first non-action word. Note, that this layout of entry fields is default and can be changed with certain options. Try to parse the file:
$ tfman t
Not a single action is recognized, despite all lines are active, because... Yes! Not a single action is specified. Now we can edit file and specify actions we desire.
Fields without action tags are ignored thus they can be removed safely. Same apply to whole lines if they are not directories (directory name is lost). Instead of removing lines we can make them inactive by replacing line tag '|' with any other character and then all actions in that line are switched off.
Edit textual representation (i have deleted unnecessary entry fileds for readability, you can do the same):
# [Dir1] | [ ../] | [Subdir/] | [ f1] >>Subdir :rwxr--r-- # [Dir1/Subdir] | [ ../] | [SubdirA/] | [SubdirB/] | [subdirC/] # [Dir1/Subdir/SubdirA] | [ ../] | [SubdirX/] | [SubdirY/] | [ e1] >>.. :rw-r--r-- # [Dir1/Subdir/SubdirA/SubdirX] | [../] | [ex] >>../.. :rw-rw-rw- | [ey] >>../.. :rw-rw-rw- # [Dir1/Subdir/SubdirA/SubdirY] | [../] | [ez] >>../.. :rw-rw-rw- # [Dir1/Subdir/SubdirB] | [../] | [e2] >>.. :rw-rw-rw- | [e3] >>.. :rw-rw-rw- # [Dir1/Subdir/subdirC] | [../] # [Dir2] * | [../] | [f2] >>../Dir1/Subdir :rw-rw-rw- | [f3] >>../Dir1/Subdir :rw-rw-rw-
All entries are moved to 'Dir1/Subdir' with changed permissions. Additionaly 'Dir2' is removed afterwards.
Now scan 'Dir1':
$ tfman Dir1 -R -C -F=name > t
and edit 't' file to look like this:
# [Dir1] Dir1 | [ e2] >>e2_new | [ e3] >>e3_new # [Dir1/Subdir] Dir1/Subdir | [ e1] >>e1_new | [ ex] >>ex_new | [ ey] >>ey_new | [ ez] >>ez_new | [ f1] >>f1_new | [ f2] >>f2_new | [ f3] >>f3_new
Parse it:
tfman t
It will rename all files.
I will try to present some tasks that - i think - you can acomplish easier and quicker then with standard command line utilities. Especially if you are using decent text editor. These are examples based on my file system, your results will certainly look different but rules stay the same.
To create whole directory tree (maybe project structure) with desired permissions and owners, write text below to file and parse it with Tfman:
$ Projname ! * PRIORITY ENTRY # Projname == ~ john:developers:rwxrws--- # subdir1 lenny:testers:rwxr-S--- | fileX # subdirA == will:testers:rwxr-S--- | file{A,B} | file{1,2} # subdir2 henry:testers:rwxr-S--- | file3
First line actions affect all entries in file thanks to '==' tag. The same tag let us change permissions and ownership for 'subdirA' and its content. We also change permissions and owners for few single lines. But what the hell the first line is for? This is priority line, thanks to line tag '$'. This type of line is executed before any other normal lines. It will remove 'Projname' and its content before proper directory is created. Thanks to that, Tfman will not terminate trying to create existing directory. But what if 'Projname' does not exist? Tfman would terminate trying to remove it. We prevent this by putting '!', tag that causes succeding tags to ignore errors. Strictly speaking, Tfman does not ignore errors, but only prints information instead of terminating.
$ Projname ! * PRIORITY ENTRY {{ # Projname # subdir1 lenny:testers:rwxr-S--- | fileX {{ # subdirA | file{A,B} | file{1,2} }} will:testers:twxr-S--- # subdir2 henry:testers:rwxr-S--- | file3 }} ~ john:developers:rwxrws---
Above achieves exactly the same but uses grouping, technique the one may find more clear.
To move multiple, selected files at once, scan part of filesystem where you want to do that and then add '>>' tag where desired. For example, I have scanned current directory:
$ tfm . -F=name
And then, after editing result, i got this:
| [.] rename files | [file1] >>f1 | [fi2e1] >>f2 | [fi3eA] >>f3 move files to common destination dir { | [file1] | [fi2e1] | [fi3eA] } >>/tmp move similarly named files from different directory here | [/tmp/file{a,b,c}*] >>.
To copy many files proceed in similar way, but this time use Tfman's ability to clone names:
$ tfm . "-F=name -C"
I got this:
| [file1] file1 | [file2] file2 | [file3] file3 | [file4] file4 ...
As you can see, all names are cloned to help you add actions like copy or move, specificaly if you rename many files and just want to slightly adjust their names. For example:
| [file1] ))file1.bak ++ :rw------- | [file2] ))file2.bak ++ :rw------- | [file3] ))file3.bak ++ :rw------- | [file4] ))file4.bak ++ :rw------- ...
This will copy every entry to file with appended suffix. We decided to change target file's permissions, and we used '++' tag for that - it makes succeding actions affect previous action's target. How to explain this clearly? If we would ommit '++', permissions change would affect entry before it is copied and both, entry and target, would have their permissions changed. With '++' change affects only target.
Move and copy files between directories:
$ tfm /dir1 /dir2 /dir3 -F=name # /dir1 | [f1] >>/dir2 move to other directory | [f2] >>./dir3 move to subdirectory in current directory # /dir2 | [fA] >>/dir1/fB move under changed name # /dir3 { | [fa] | [fb] | [fc] } >>/dir2 move all grouped files # /dir4 = >>/dir2 move all entries under directory but not directory itself | [e1] | [e2]
Remove selected files:
$ tfm ./subdir{1,2,3}.ver* -F=name # subdir1.ver12 | *[file1] rm file | [file2] | [file3] | *[file4] rm file # subdir2.ver2 { | [file2] | [file3] } * rm group | [file4]
Change permissions of multiple files:
| [file1] :rwx------ | [file2] :rwxrwx--- | [file3] :rw-rw-r--
This chapter have to be improved. Separate tutorial is needed as well.
Tfman copy vim syntax file and file management plugin into Vim's system directories during installation. Former give you colors during editing of textual representation, later give you ability to manage files through Tfman from within Vim.
To switch on highlighting for particular file, issue following command in Vim:
:set filetype=tfm
To switch it on automatically, every time you edit file with extension '.tfm', place this in ~/.vimrc file:
autocmd BufRead,BufNewFile *.tfm set filetype=tfm
Small plugin that provides multi-window, textual file management from within Vim. Few commands and mappings for opening scan results in windows, editing textual representation efficiently, parsing content and showing parse results. It even provides commands to perform operations for entry under cursor, similarly to standard file managers. Allows moving up in directories hierarchy and handles simple history of scanned directories, to move back and forward between them.
You can edit textual representation as normal text.
Following commands and mappings are implemented for now:
History, with numbers and directory names is printed at top of every scan result.
This short script combines both Tfman modes. It scans given directories, opens result in your favorite text editor and parses edited result after you quit. Editor is determined from EDITOR environment variable. First arguments are directories to scan, rest is passed to Tfman as options. For example:
$ tfm dir1 dir2 dir3 -R -F=name,type --show-only
Format of textual representation is strict and consist of entry lines. Each entry line describes one file - its name, type, properties etc. Lines are marked with line tags determining line type. It can be normal line ("|" or space " "), priority line ("$") or directory ("#"). If first non-blank character in line is not line tag, line is ignored at all.
# dir # subdir | entry_name entry_name line_inactive .line_inactive *line_inactive
Any other character placed as first non-blank character will cause line to be ignored by parser.
Indentation doesn't matter for entries ('|', '$'), because they always belong to directory above them. Indentation matters only for directories ('#'). It determines its parent.
If it is not indented it is new parent directory. If it is on same indentation level as previous directory, it becomes child of its parent. If it is more indented, it becomes child of previous directory. If it is less indented, it becomes child of previous directory's ancestor, as many levels back in hierarchy as many levels of indentation it is shifted left.
# Dir # SubdirA becomes...> Dir/SubdirA # SubdirB becomes...> Dir/SubdirB # Dir2 # SubdirA becomes...> Dir2/SubdirA # SubdirB becomes...> Dir2/SubdirA/SubdirB # SubdirC becomes...> Dir2/SubdirC
Understanding indentation may be a bit tricky. Just stick to following rules:
| [name]>>operand | [name]* | name >operand | name *Active lines are parsed for actions, encountered actions are grouped by type and performed in groups, type after type, in fixed order. Actions of same type are performed in order of appearance in text. For available actions consult manpage.
Composition of entry names and action tag's operands can be done in three ways: absolute name, name relative to execution directory, name relative to in-text parent directory. Entry names and operands being file names have parent directory prepended before actions take place, except if name is absolute path or begins with './'. Note that './' alone is exception to this exception - it has directory prepended.
# Dir name >>/target virtually becomes: Dir/name >>/target ./name >>./target virtually becomes: /home/uli/name >>/home/uli/target # Subdir /name >>./ virtually becomes: /name >>Dir/Subdir/./
In above example Tfman was invoked in '/home/uli' directory.
Why is it designed this way? Because somebody may want to operate on names relatively to program execution (current working) directory or operate on absolute names, avoiding prepending parent directory.
Brace expansion and globbing is available for entry names. Procedure of preparing entry name is as follows: first braces are expanded, then subdirectory and directory names are prepended if required, then resulted names are subject to globbing.
Brace expansion is performed on entry names similarly to normal shell brace expansion:
| [name{ab,cd}] will expand to nameab namecd
Globbing is performed on entry names in the same way as normal shell globbing:
| [file*] will expand to all filenames starting with word file''
Brace expansion has precedence over globbing:
| [file{1,2}*] will expand to file1* file2*, then globbing occurs
Operands undergo only prepending of directories, they are not subject to brace expansion or globbing.
# dir | [*file{A,B}] >>target
Results in 'dir/*fileA' and 'dir/*fileB' being globbed and then resulting files are moved to 'dir/target'.
Group entries to define actions for all of them at once:
{ # Dir it is not moved because single brackets do not include directories | [e1] | [e2] >>dir2 individual action has precedece over group actions | [e3] } >>dir1
It will move all entries to dir1, except e2 which will be moved to dir2. As you can see, actions specified for individual entries have precedence over actions specified for group.
But what if directory needs to be included in group? Use strong grouping:
{{ # Dir | [e1] | [e2] | [e3] }} nuno:
It will change user for directory and all entries.
# Dir oli: | entry
Above will change user for directory only.
# Dir = oli: | entry
Above will change user for directory content (entries given below), not for directory itself. We have used '=' tag for that. It changes behavior of all following actions.
# Dir == oli: | entry
Above will change user for directory and for its content. We have used '.=' tag for that. It changes behavior of all following actions.