0%

查看庐山真面貌的cat

.. note::
此去经年,应是良辰好景虚设
宋 柳永《雨霖铃》

cat命令可用于输出文件的内容到标准输出。

cat的官方定义为:

concatenate files and print on the standard output

翻译过来就是:把档案串连接后传到基本输出

其用法一般为:

1
$ cat [OPTION]... [FILE]...

cat命令的可选参数[OPTION]如下所示:

  • -n--number: 由 1 开始对所有输出的行数编号
  • -b--number-nonblank: 和 -n 相似,只不过对于空白行不编号
  • -s --squeeze-blank :当遇到有连续两行以上的空白行,就代换为一行的空白行
  • -T--show-tabs:显示TAB字符,显示为^I
  • -E--show-ends:显示行末符号,字符为$
  • -A--show-all:显示所有的信息

此时假定我们的文件为hello.c,内容为最经典的:

1
2
3
4
5
6
7
8
#include <stdio.h>

int main(int argc, char * argv[])
{
printf("Hello World\n");

return 0;
}

接下来的实例全部根据这个文件展开,Hello World. Hello Linux

实例 :简单显示内容

1
2
3
4
5
6
7
8
9
10
11
$ cat hello.c 

#include <stdio.h>

int main(int argc, char * argv[])
{
printf("Hello World\n");

return 0;
}

实例 :显示行号 -n

1
2
3
4
5
6
7
8
9
10
11
$ cat -n hello.c 

1 #include <stdio.h>
2
3 int main(int argc, char * argv[])
4 {
5 printf("Hello World\n");
6
7 return 0;
8 }

实例 : 显示行末

1
2
3
4
5
6
7
8
9
$ cat -E hello.c 
#include <stdio.h>$
$
int main(int argc, char * argv[])$
{$
printf("Hello World\n");$
$
return 0;$
}$

实例:显示空白字符

1
2
3
4
5
6
7
8
9
cat -T hello.c 
#include <stdio.h>

int main(int argc, char * argv[])
{
^Iprintf("Hello World\n");
^I
^Ireturn 0;
}

此时可以看到^I,which means Tab charcter.

加一个管道

比如,此时希望看到你的源码文件一共多少行,每行代表什么意思,就可以把含有行号的输入通过管道发送到另外一个文件,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ cat -n hello.c > hello_number.c

$ cat hello_number.c

1 #include <stdio.h>
2
3 int main(int argc, char * argv[])
4 {
5 printf("Hello World\n");
6
7 return 0;
8 }

其他的一些选项可以自行尝试。

cat 显示文件内容

cat命令可用于输出文件的内容到标准输出。

cat的官方定义为:

concatenate files and print on the standard output

翻译过来就是:把档案串连接后传到基本输出

其用法一般为:

1
$ cat [OPTION]... [FILE]...

cat命令的可选参数[OPTION]如下所示:

  • -n--number: 由 1 开始对所有输出的行数编号
  • -b--number-nonblank: 和 -n 相似,只不过对于空白行不编号
  • -s --squeeze-blank :当遇到有连续两行以上的空白行,就代换为一行的空白行
  • -T--show-tabs:显示TAB字符,显示为^I
  • -E--show-ends:显示行末符号,字符为$
  • -A--show-all:显示所有的信息

此时假定我们的文件为hello.c,内容为最经典的:

1
2
3
4
5
6
7
8
#include <stdio.h>

int main(int argc, char * argv[])
{
printf("Hello World\n");

return 0;
}

接下来的实例全部根据这个文件展开,Hello World. Hello Linux

实例 :简单显示内容

1
2
3
4
5
6
7
8
9
10
11
$ cat hello.c 

#include <stdio.h>

int main(int argc, char * argv[])
{
printf("Hello World\n");

return 0;
}

实例 :显示行号 -n

1
2
3
4
5
6
7
8
9
10
11
$ cat -n hello.c 

1 #include <stdio.h>
2
3 int main(int argc, char * argv[])
4 {
5 printf("Hello World\n");
6
7 return 0;
8 }

实例 : 显示行末

1
2
3
4
5
6
7
8
9
$ cat -E hello.c 
#include <stdio.h>$
$
int main(int argc, char * argv[])$
{$
printf("Hello World\n");$
$
return 0;$
}$

实例:显示空白字符

1
2
3
4
5
6
7
8
9
cat -T hello.c 
#include <stdio.h>

int main(int argc, char * argv[])
{
^Iprintf("Hello World\n");
^I
^Ireturn 0;
}

此时可以看到^I,which means Tab charcter.

加一个管道

比如,此时希望看到你的源码文件一共多少行,每行代表什么意思,就可以把含有行号的输入通过管道发送到另外一个文件,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ cat -n hello.c > hello_number.c

$ cat hello_number.c

1 #include <stdio.h>
2
3 int main(int argc, char * argv[])
4 {
5 printf("Hello World\n");
6
7 return 0;
8 }

其他的一些选项可以自行尝试。

你或他是否可读写执行的 chmod

.. _linux_chmod_beginner:

.. note::
不以规矩,不能成方圆。
《孟子·离娄上》

文件权限指的是文件是否可以执行、写入、读取等操作。

该命令官方定义为:

chmod - change mode

所以可以通过chmod来控制文件如何被他人所存取。

而Linux/Unix的文件存取权限分为三级 : 文件所有者、用户组及其他,分别使用以下字母来表示:

  • u:所有者
  • g:用户组
  • o:其他用户

如下图所示,每个级别都可以设置为rwx三种权限 。

Linux chmod

命令格式

使用的语法如下所示:

1
$ chmod [-cfvR] [--help] [--version] mode file...

其中mode权限设定的格式如下 : [ugoa] [+-=] [rwxX]

其中u表示该文件的拥有者,g表示与该文件的拥有者属于同一个群体(group)者,o表示其他以外的人,a表示这三者皆是。

  • + 表示加权限、 表示减权限、= 表示设定唯一权限。
  • r 表示可读取,w 表示可写入,x 表示可执行,X 表示只有当该文件是个子目录或者该文件已经被设定过为可执行。
  • -R : 对目前目录下的所有文件与子目录进行相同的权限变更(即以递回的方式逐个变更)

对于chmod的使用而言,只有文件所有者和超级用户可以修改文件或目录的权限。

具体的方法为可以使用符号模式或者绝对模式来进行操作。

而我比较喜欢用的是绝对数字模式,粗暴简单易理解。

符号模式

使用符号模式需要考虑多个因素,其中包括用户类型,操作符 和设定权限。

who 用户类型 说明
u user 文件所有者
g group 文件所有者所在组
o others 所有其他用户
a all 所用用户, 相当于 ugo

operator 的符号模式表:

Operator 说明
+ 为指定的用户类型增加权限
- 去除指定用户类型的权限
= 设置指定用户权限的设置,即将用户类型的所有权限重新设置

permission 的符号模式表:

模式 名字 说明
r 设置为可读权限
w 设置为可写权限
x 执行权限 设置为可执行权限

绝对数字模式

chmod命令可以使用八进制数来指定权限。文件或目录的权限位是由9个权限位来控制,每三位为一组,它们分别是文件所有者的读、写、执行权限,用户组的读、写、执行以及其它用户的读、写、执行。

历史上,文件权限被放在一个比特掩码中,掩码中指定的比特位设为1,用来说明一个类具有相应的优先级。比如下面的0-7分别表示各自的权限定义。

No 权限 rwx 二进制
7 读 + 写 + 执行 rwx 111
6 读 + 写 rw- 110
5 读 + 执行 r-x 101
4 只读 r– 100
3 写 + 执行 -wx 011
2 只写 -w- 010
1 只执行 –x 001
0 000

如表所示:

  • 对于7而言就表示所有者的权限为可读、可写、可执行,也就是对应的八进制为111,所以就是7;
  • 而对于 5而言,表示所有者的权限为可读、可执行,对应的八进制为101,所以就是5;

其他类似。

实例

更改为全部可读

接下来将文件 a.c 设为所有人皆可读取 ,有三种方式可以使用,如下,分别为 :

  1. chmod ugo+r filename
  2. chmod a+r filename
  3. chmod 444 filename

具体如下所示:

通过方法1:

1
2
3
4
5
6
7
8
9
# 默认设定为没有任何属性
$ ll
---------- 1 user user 5KB Feb 12 22:22 a.c

# 更改为全部可读
$ chmod ugo+r file1.txt

$ ll
-r--r--r-- 1 user user 5KB Feb 12 22:22 a.c

通过方法2:

1
2
3
4
5
6
7
8
9
# 默认设定为没有任何属性
$ ll
---------- 1 user user 5KB Feb 12 22:23 a.c

# 更改为全部可读
$ chmod a+r file1.txt

$ ll
-r--r--r-- 1 user user 5KB Feb 12 22:23 a.c

通过方法3:

1
2
3
4
5
6
7
8
9
# 默认设定为没有任何属性
$ ll
---------- 1 user user 5KB Feb 12 22:23 a.c

# 更改为全部可读
$ chmod 444 file1.txt

$ ll
-r--r--r-- 1 user user 5KB Feb 12 22:24 a.c

设置用户及组可读写,其他用户无法写入但可以查看

接下来继续把文件 a.c设置为用户 和组可以读写,而其他 用户无法写入但是 可以查看 。

使用符号模式如下:

1
2
3
4
5
6
7
$ ll
-r--r--r-- 1 user user 5KB Feb 12 22:24 a.c

$ chmod ug+rw,o+r,o-w a.c

$ ll
-rw-rw-r-- 1 user user 5KB Feb 12 22:26 a.c

使用数字模式如下:

1
2
3
4
5
6
7
$ ll
-r--r--r-- 1 user user 5KB Feb 12 22:24 b.c

$ chmod 664 a.c

$ ll
-rw-rw-r-- 1 user user 5KB Feb 12 22:26 b.c

设定为所有人只有可执行权限

此时不管文件的权限是什么,因为只具有可执行权限,所以符号模式可以使用**=**,而数字模式只需要1即可,如下:

1
2
3
4
5
6
7
$ chmod a=x filename
#或者
$ chmod 111 filename

# 无法读取
$ cat a.c
cat: a.c: Permission denied

所以对于只有可执行权限的文件,是无法执行读取或者写入操作的,这也保证了文件的安全性。

文件权限设置命令 chmod

文件权限指的是文件是否可以执行、写入、读取等操作。

而Linux/Unix的文件存取权限分为三级 : 文件所有者、用户组及其他,分别使用以下字母来表示:

  • u:所有者
  • g:用户组
  • o:其他用户

如下图所示,每个级别都可以设置为rwx三种权限 。

Linux chmod

改命令官方的解释为:

change mode

所以可以通过chmod来控制文件如何被他人所存取。

使用的语法如下所示:

1
$ chmod [-cfvR] [--help] [--version] mode file...

其中mode权限设定的格式如下 : [ugoa] [+-=] [rwxX]

其中u表示该文件的拥有者,g表示与该文件的拥有者属于同一个群体(group)者,o表示其他以外的人,a表示这三者皆是。

  • + 表示加权限、 表示减权限、= 表示设定唯一权限。
  • r 表示可读取,w 表示可写入,x 表示可执行,X 表示只有当该文件是个子目录或者该文件已经被设定过为可执行。
  • -R : 对目前目录下的所有文件与子目录进行相同的权限变更(即以递回的方式逐个变更)

对于chmod的使用而言,只有文件所有者和超级用户可以修改文件或目录的权限。

具体的方法为可以使用符号模式或者绝对模式来进行操作。

而我比较喜欢用的是绝对数字模式,比较粗暴简单。

符号模式

使用符号模式需要考虑多个因素,其中包括用户类型,操作符 和设定权限。

who 用户类型 说明
u user 文件所有者
g group 文件所有者所在组
o others 所有其他用户
a all 所用用户, 相当于 ugo

operator 的符号模式表:

Operator 说明
+ 为指定的用户类型增加权限
- 去除指定用户类型的权限
= 设置指定用户权限的设置,即将用户类型的所有权限重新设置

permission 的符号模式表:

模式 名字 说明
r 设置为可读权限
w 设置为可写权限
x 执行权限 设置为可执行权限

绝对数字模式

chmod命令可以使用八进制数来指定权限。文件或目录的权限位是由9个权限位来控制,每三位为一组,它们分别是文件所有者的读、写、执行权限,用户组的读、写、执行以及其它用户的读、写、执行。历史上,文件权限被放在一个比特掩码中,掩码中指定的比特位设为1,用来说明一个类具有相应的优先级。比如下面的0-7分别表示各自的权限定义。

No 权限 rwx 二进制
7 读 + 写 + 执行 rwx 111
6 读 + 写 rw- 110
5 读 + 执行 r-x 101
4 只读 r– 100
3 写 + 执行 -wx 011
2 只写 -w- 010
1 只执行 –x 001
0 000

如表所示:

  • 对于7而言就表示所有者的权限为可读、可写、可执行,也就是对应的八进制为111,所以就是7;
  • 而对于 5而言,表示所有者的权限为可读、可执行,对应的八进制为101,所以就是5;

其他类似。

实例更改为全部可读

接下来将文件 a.c 设为所有人皆可读取 ,有三种方式可以使用,如下,分别为 :

  1. chmod ugo+r filename
  2. chmod a+r filename
  3. chmod 444 filename

具体如下所示:

通过方法1:

1
2
3
4
5
6
7
8
9
# 默认设定为没有任何属性
$ ll
---------- 1 user user 5KB Feb 12 22:22 a.c

# 更改为全部可读
$ chmod ugo+r file1.txt

$ ll
-r--r--r-- 1 user user 5KB Feb 12 22:22 a.c

通过方法2:

1
2
3
4
5
6
7
8
9
# 默认设定为没有任何属性
$ ll
---------- 1 user user 5KB Feb 12 22:23 a.c

# 更改为全部可读
$ chmod a+r file1.txt

$ ll
-r--r--r-- 1 user user 5KB Feb 12 22:23 a.c

通过方法3:

1
2
3
4
5
6
7
8
9
# 默认设定为没有任何属性
$ ll
---------- 1 user user 5KB Feb 12 22:23 a.c

# 更改为全部可读
$ chmod 444 file1.txt

$ ll
-r--r--r-- 1 user user 5KB Feb 12 22:24 a.c

设置用户及组可读写,其他用户无法写入但可以查看

接下来继续把文件 a.c设置为用户 和组可以读写,而其他 用户无法写入但是 可以查看 。

使用符号模式如下:

1
2
3
4
5
6
7
$ ll
-r--r--r-- 1 user user 5KB Feb 12 22:24 a.c

$ chmod ug+rw,o+r,o-w a.c

$ ll
-rw-rw-r-- 1 user user 5KB Feb 12 22:26 a.c

使用数字模式如下:

1
2
3
4
5
6
7
$ ll
-r--r--r-- 1 user user 5KB Feb 12 22:24 b.c

$ chmod 664 a.c

$ ll
-rw-rw-r-- 1 user user 5KB Feb 12 22:26 b.c

设定为所有人只有可执行权限

此时不管文件的权限是什么,因为只具有可执行权限,所以符号模式可以使用**=**,而数字模式只需要1即可,如下:

1
2
3
4
5
6
7
$ chmod a=x filename
#或者
$ chmod 111 filename

# 无法读取
$ cat a.c
cat: a.c: Permission denied

所以对于只有可执行权限的文件,是无法执行读取或者写入操作的,这也保证了文件的安全性。

炫技 - TODO

其实对于每个文件或者目录而言,除了rwx权限,还有 一个权限位,这个权限为一般为特殊权限。

模式 名字 说明
X 特殊执行权限 只有当文件为目录文件,或者其他类型的用户有可执行权限时,才将文件权限设置可执行
s setuid/gid 当文件被执行时,根据who参数指定的用户类型设置文件的setuid或者setgid权限
t 粘贴位 设置粘贴位,只有超级用户可以设置该位,只有文件所有者u可以使用该位

若用 chmod 4755 filename 可使此程序具有 root 的权限。

更多说明

命令 说明
chmod 4755 *file* 4设置了设置用户ID位,剩下的相当于 u=rwx (4+2+1),go=rx (4+1 & 4+1)。
find path/ -type d -exec chmod a-x {} \; 删除可执行权限对path/以及其所有的目录(不包括文件)的所有用户,使用’-type f’匹配文件
find path/ -type d -exec chmod a+x {} \; 允许所有用户浏览或通过目录path/

通过其他文件修改模式

如果有1个文件或文件夹的权限是你认为可以参考的,此时可以通过下面的命令直接指定,而不需要指定user:group这个参数了。如下两个命令效果一致:

1
2
3
4
5
6
7
8
9
$ ll 
-rw-rw-r--. 1 user1 group1 5 May 7 14:56 a
-rw-rw-r--. 1 user2 group2 5 May 7 14:56 b

# 将b的权限更改为与a一致
# Method1
$ sudo chown user1:group1 b
# Method 2
$ sudo chown --reference=a b

进入数字的魔幻世界:探秘cmatrix

来到黑客帝国的命令cmatrix,这不仅仅是一串字符在终端中的运动,它仿佛是数字的舞蹈,变幻着无穷的图案。从绿色的数字雨到模拟Matrix电影的效果,cmatrix让我们进入了数字的奇妙世界。

安全起见,拷贝为先 - cp

.. _linux_cp_beginner:

.. note::
昔去雪如花,今来花似雪。
范云《别诗》

cp命令很简单,字面的意思,copy的缩写,意指拷贝数据。

官方含义为:

cp - copy files and directories

– 拷贝文件和文件夹。

命令格式

简单的格式如下所示,cp后面跟上选项,然后是SRC,最后是DEST

1
$ cp [option]... SOURCE... DIRECTORY

下面说几个最常用的选项实例。

  • -a:保留链接、文件属性,并复制目录下的所有内容,类似于等于dpr参数组合
  • -d:复制时保留链接
  • -f:覆盖已经存在的目标文件而不给出提示
  • -i:与-f选项相反,要求用户确认是否覆盖
  • -p:除复制文件的内容外,还把修改时间和访问权限也复制到新文件
  • -r:若给出的源文件是一个目录文件,此时将复制该目录下所有的子目录和文件

首先假设有两个文件夹dir1dir2,里面的内容如下所示:

1
2
3
4
5
6
7
8
9
10
11
dir1
├── a
├── b
├── c
└── d
dir2
├── b
├── d
└── e

0 directories, 7 files

详细信息如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ ll *
dir1:
total 0
-rw-rw-r-- 1 user user 0 Jul 20 21:23 a
-rw-rw-r-- 1 user user 0 Jul 20 21:23 b
-rw-rw-r-- 1 user user 0 Jul 20 21:23 c
-rw-rw-r-- 1 user user 0 Jul 20 21:23 d

dir2:
total 0
-rw-rw-r-- 1 user user 0 Jul 20 21:25 b
-rw-rw-r-- 1 user user 0 Jul 20 21:25 d
-rw-rw-r-- 1 user user 0 Jul 20 21:25 e

cp最常用的选项如下所示:

  • i : 覆盖一个已经存在的文件前,提示用户进行确认
  • r:递归地复制目录及其内容,复制目录的时候必须使用这个参数
  • u:只复制不存在或者更新的文件
  • v:复制文件时,显示复制信息

组合rv - 可以拷贝文件或文件夹

这个在显示复制信息的时候,也可以复制目录

1
2
3
4
5
6
$ cp -rv dir1/* dir2/
‘dir1/a’ -> ‘dir2/a’
‘dir1/b’ -> ‘dir2/b’
‘dir1/c’ -> ‘dir2/c’
‘dir1/d’ -> ‘dir2/d’

拷贝时提示确认

这个参数在使用rm的时候已经记得使用,不然就像rm -rf /一样,一个公司没有了。

1
2
3
4
$ cp -i dir1/* dir2/
cp: overwrite ‘dir2/a’? y
cp: overwrite ‘dir2/b’? y
cp: overwrite ‘dir2/c’? y

这个选项在文件超级多时候,慎用!!

只拷贝不存在或更新的文件

u表示update,也就是从一个目录拷贝到另外一个目录时,只会复制那些不存在或者目标目录相应文件的更新文件。

执行下面的命令:

1
$ cp -u dir1/* dir2/

可以得到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ ll *
dir1:
total 0
-rw-rw-r-- 1 user user 0 Jul 20 21:23 a
-rw-rw-r-- 1 user user 0 Jul 20 21:23 b
-rw-rw-r-- 1 user user 0 Jul 20 21:23 c
-rw-rw-r-- 1 user user 0 Jul 20 21:23 d

dir2:
total 0
-rw-rw-r-- 1 user user 0 Jul 20 21:29 a
-rw-rw-r-- 1 user user 0 Jul 20 21:25 b
-rw-rw-r-- 1 user user 0 Jul 20 21:29 c
-rw-rw-r-- 1 user user 0 Jul 20 21:25 d
-rw-rw-r-- 1 user user 0 Jul 20 21:25 e

如何用cp拷贝指定序号的文件

现在有文件夹filename,内有文档,名字是从1.txt, 2.txt, 3.txt 一直到9999.txt,10000.txt,现在希望从第N组数据即N.txt到第M组数据M.txt的文件拷贝到别的文件夹中,方法如下:

1
$ cp {N..M}.txt   newfilename/

这个方法可是相当的赞呀(≧▽≦)/,基本可以秒掉大多数的GUI程序了。

Linux 的cp命令

cp命令很简单,字面的意思,copy的缩写,意指拷贝数据。

官方含义为:

cp - copy files and directories

– 拷贝文件和文件夹。

命令格式

简单的格式如下所示,cp后面跟上选项,然后是SRC,最后是DEST

1
$ cp [option]... SOURCE... DIRECTORY

下面说几个最常用的选项实例。

  • -a:保留链接、文件属性,并复制目录下的所有内容,类似于等于dpr参数组合
  • -d:复制时保留链接
  • -f:覆盖已经存在的目标文件而不给出提示
  • -i:与-f选项相反,要求用户确认是否覆盖
  • -p:除复制文件的内容外,还把修改时间和访问权限也复制到新文件
  • -r:若给出的源文件是一个目录文件,此时将复制该目录下所有的子目录和文件

首先假设有两个文件夹dir1dir2,里面的内容如下所示:

1
2
3
4
5
6
7
8
9
10
11
dir1
├── a
├── b
├── c
└── d
dir2
├── b
├── d
└── e

0 directories, 7 files

详细信息如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ ll *
dir1:
total 0
-rw-rw-r-- 1 user user 0 Jul 20 21:23 a
-rw-rw-r-- 1 user user 0 Jul 20 21:23 b
-rw-rw-r-- 1 user user 0 Jul 20 21:23 c
-rw-rw-r-- 1 user user 0 Jul 20 21:23 d

dir2:
total 0
-rw-rw-r-- 1 user user 0 Jul 20 21:25 b
-rw-rw-r-- 1 user user 0 Jul 20 21:25 d
-rw-rw-r-- 1 user user 0 Jul 20 21:25 e

cp最常用的选项如下所示:

  • i : 覆盖一个已经存在的文件前,提示用户进行确认
  • r:递归地复制目录及其内容,复制目录的时候必须使用这个参数
  • u:只复制不存在或者更新的文件
  • v:复制文件时,显示复制信息

组合rv - 可以拷贝文件或文件夹

这个在显示复制信息的时候,也可以复制目录

1
2
3
4
5
6
$ cp -rv dir1/* dir2/
‘dir1/a’ -> ‘dir2/a’
‘dir1/b’ -> ‘dir2/b’
‘dir1/c’ -> ‘dir2/c’
‘dir1/d’ -> ‘dir2/d’

拷贝时提示确认

这个参数在使用rm的时候已经记得使用,不然就像rm -rf /一样,一个公司没有了。

1
2
3
4
$ cp -i dir1/* dir2/
cp: overwrite ‘dir2/a’? y
cp: overwrite ‘dir2/b’? y
cp: overwrite ‘dir2/c’? y

这个选项在文件超级多时候,慎用!!

只拷贝不存在或更新的文件

u表示update,也就是从一个目录拷贝到另外一个目录时,只会复制那些不存在或者目标目录相应文件的更新文件。

执行下面的命令:

1
$ cp -u dir1/* dir2/

可以得到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ ll *
dir1:
total 0
-rw-rw-r-- 1 user user 0 Jul 20 21:23 a
-rw-rw-r-- 1 user user 0 Jul 20 21:23 b
-rw-rw-r-- 1 user user 0 Jul 20 21:23 c
-rw-rw-r-- 1 user user 0 Jul 20 21:23 d

dir2:
total 0
-rw-rw-r-- 1 user user 0 Jul 20 21:29 a
-rw-rw-r-- 1 user user 0 Jul 20 21:25 b
-rw-rw-r-- 1 user user 0 Jul 20 21:29 c
-rw-rw-r-- 1 user user 0 Jul 20 21:25 d
-rw-rw-r-- 1 user user 0 Jul 20 21:25 e

如何用cp拷贝指定序号的文件

现在有文件夹filename,内有文档,名字是从1.txt, 2.txt, 3.txt 一直到9999.txt,10000.txt,现在希望从第N组数据即N.txt到第M组数据M.txt的文件拷贝到别的文件夹中,方法如下:

1
$ cp {N..M}.txt   newfilename/

这个方法可是相当的赞呀(≧▽≦)/,基本可以秒掉大多数的GUI程序了。

dup2 函数的使用详解

dup2 是 Unix/Linux 系统中用于复制文件描述符的函数,它允许将一个文件描述符复制到另一个文件描述符上,实现输入输出流的重定向操作,比如将stdout重定向到一个文件。

函数原型与头文件

1
2
3
#include <unistd.h>

int dup2(int oldfd, int newfd);

参数说明

  • **oldfd**:需要复制的源文件描述符(比如已经打开的文件、管道、套接字等)。
  • **newfd**:目标文件描述符,用于接收oldfd的副本或者重定向的描述符。若newfd已打开,系统会先关闭它,再进行复制。

返回值

  • 成功时:返回新的文件描述符(即newfd,若newfd之前已关闭则可能返回其他值)。
  • 失败时:返回-1,并设置errno错误码(如EBADF表示oldfd无效,EMFILE表示文件描述符耗尽)。

核心功能与原理

dup2 的核心作用是让newfd成为oldfd的副本,即让两个文件描述符指向同一个文件表项,从而共享文件偏移量和状态标志。例如:

  • newfd重定向为oldfd的副本后,对newfd的读写操作等同于对oldfd的操作。
  • 常见场景是重定向标准输入(STDIN_FILENO=0)、标准输出(STDOUT_FILENO=1)、标准错误(STDERR_FILENO=2)。

使用示例:重定向标准输出到文件

下面的这个程序会将原本输出到终端的重定向输出到一个文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/**
* @file linux-dup2-to-file.c
* @brief redirect stdout to file
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int main() {
int fd, old_stdout;
char *filename = "dup2-to-file-output.txt";

// keep the original stdout for restore
old_stdout = dup(STDOUT_FILENO);
if (old_stdout == -1) {
perror("dup failed");
exit(EXIT_FAILURE);
}

// open the file for writing
fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1) {
perror("open failed");
exit(EXIT_FAILURE);
}

// redirect stdout to file
if (dup2(fd, STDOUT_FILENO) == -1) {
perror("dup2 failed");
exit(EXIT_FAILURE);
}
close(fd); // close fd, because STDOUT_FILENO is now pointing to it

// now printf's output will be written to file
printf("This text will be written to %s\n", filename);
printf("Another line for testing...\n");

// Remember to flush the buffer otherwise the output will be buffered
fflush(stdout);

// restore original stdout
if (dup2(old_stdout, STDOUT_FILENO) == -1) {
perror("dup2 restore failed");
exit(EXIT_FAILURE);
}
// close the backup descriptor
close(old_stdout);

// after restore, output to terminal
printf("Now back to terminal output\n");

return 0;
}

编译使用的方法如下所示:

1
2
3
4
5
6
7
8
$ gcc -o linux-dup2-to-file linux-dup2-to-file.c

$ ./linux-dup2-to-file
Now back to terminal output

$ cat dup2-to-file-output.txt
This text will be written to dup2-to-file-output.txt
Another line for testing...

需要注意的是,在执行完操作后,需要调用fflush(stdout)强制刷新,不然可能不是你希望的现象。

使用示例:重定向标准输入从文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int main() {
int fd, old_stdin;
char *filename = "input.txt";
char buffer[1024];

// 1. 保存原始标准输入描述符
old_stdin = dup(STDIN_FILENO);
if (old_stdin == -1) {
perror("dup failed");
exit(EXIT_FAILURE);
}

// 2. 打开文件用于读取
fd = open(filename, O_RDONLY);
if (fd == -1) {
perror("open failed");
exit(EXIT_FAILURE);
}

// 3. 将标准输入重定向到文件
if (dup2(fd, STDIN_FILENO) == -1) {
perror("dup2 failed");
exit(EXIT_FAILURE);
}
close(fd); // 关闭fd,因为STDIN_FILENO已指向它

// 4. 此时scanf从文件读取数据
printf("Reading from %s:\n", filename);
while (fgets(buffer, sizeof(buffer), stdin) != NULL) {
printf("Read: %s", buffer);
}

// 5. 恢复原始标准输入
if (dup2(old_stdin, STDIN_FILENO) == -1) {
perror("dup2 restore failed");
exit(EXIT_FAILURE);
}
close(old_stdin);

// 6. 恢复后从终端输入
printf("Enter something from terminal: ");
fgets(buffer, sizeof(buffer), stdin);
printf("You entered: %s", buffer);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
cat dup2-from-file-input.txt 
Roses are red,
Violets are blue,
Testing dup2,
And stdin too.%
➜ build git:(main) ✗ ./linux-dup2-from-file
Reading from dup2-from-file-input.txt:
[Read] : Roses are red,
[Read] : Violets are blue,
[Read] : Testing dup2,
[Read] : And stdin too.

[Terminal] Enter something: [Terminal] You entered: hello world

dup函数的区别

dup 函数(int dup(int oldfd))也是用于复制文件描述符,但两者有重要区别:

  • dup 返回最小的未使用文件描述符,而dup2可以指定目标描述符newfd

  • dup2(newfd, oldfd)
    
    1
    2
    3
    4
    5
    6



    ```c
    close(newfd);
    return dup(oldfd);

应用场景

  1. IO 重定向:如将程序输出重定向到文件(类似 Shell 中的> file)。
  2. 管道通信:在父子进程间通过管道传递数据时,重定向输入 / 输出。
  3. 日志系统:将程序日志输出同时写入终端和日志文件。
  4. 网络编程:将套接字描述符重定向为标准输入 / 输出,实现网络 IO 与标准 IO 的统一处理。

注意事项

  1. 文件描述符关闭顺序:重定向时若newfd已打开,dup2会先关闭它,可能导致数据丢失。
  2. 原子性dup2是原子操作,无需担心多线程环境下的竞争条件。
  3. 资源管理:复制的描述符共享文件表项,需确保正确关闭(避免文件描述符泄漏)。
  4. 错误处理:始终检查dup2的返回值,处理可能的错误(如文件不存在、权限不足)。

通过合理使用dup2,可以灵活控制程序的输入输出流向,实现复杂的系统编程需求。在实际开发中,它常与管道、套接字等结合使用,是构建进程间通信和重定向机制的核心工具。

探索Linux世界的智慧——Fortunes命令

在Linux中,有一条神奇的命令连接着智慧与幽默,那就是fortunes命令。

看似普通的指令,背后却藏匿着千言万语,无论是名人箴言还是妙趣横生的笑话,在这里都能找到它们的踪迹。

另外最重要的,还可以根据自己的需求进行增删,目前就用基于唐诗宋词的库。

fortune并非只是简单的一句话,而是承载着古今中外智慧的涌泉。

每次执行,它都会带来截然不同的感受。或许是一位哲人的深刻格言,或是一句调皮的笑话,或者唐诗,或者宋词,与先贤对话,岂不快哉。

最简单的方法就是把这个命令,加到.bashrc文件中,每次启动,总会有触动。

1
2
3
4
5
6
7
8
9
10
$ fortune 
何以称英雄?识以领其先

    - 清·袁枚
$ fortune
一件作品的固有力量从来不会被长期地埋没或禁锢。一件艺术品可能被时间遗忘,可能遭到查禁,可能被埋进棺材,但威力强大的东西总要战胜没有过大前途的东西。

    - 茨威格
$ fortune
Yow! We're going to a new disco!