AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / ubuntu / 问题 / 1190700
Accepted
BlueSkies
BlueSkies
Asked: 2019-11-22 13:01:59 +0800 CST2019-11-22 13:01:59 +0800 CST 2019-11-22 13:01:59 +0800 CST

“cat <文件名”和“cat文件名”有什么区别?

  • 772

显示文件内容的最简单方法是使用以下cat命令:

cat file.txt

我可以使用输入重定向获得相同的结果:

cat < file.txt

那么,它们之间有什么区别呢?

command-line redirect cat
  • 5 5 个回答
  • 11944 Views

5 个回答

  • Voted
  1. glenn jackman
    2019-11-22T13:53:07+08:002019-11-22T13:53:07+08:00
    cat file
    

    程序将cat打开、读取和关闭文件。

    cat < file
    

    您的 shell 将打开文件并将内容连接到cat的标准输入。cat识别它没有文件参数,并将从标准输入读取。

    • 63
  2. Best Answer
    Pilot6
    2019-11-22T13:05:56+08:002019-11-22T13:05:56+08:00

    从用户的角度来看没有区别。这些命令做同样的事情。

    从技术上讲,区别在于打开文件的程序是什么:cat程序或运行它的外壳。重定向由 shell 在运行命令之前设置。

    (所以在其他一些命令中——也就是说,不是问题中显示的命令——可能会有所不同。特别是,如果您无法访问file.txt但 root 用户可以访问,则可以sudo cat file.txt工作但sudo cat < file.txt不能。)

    您可以使用在您的情况下方便的任何一种。

    几乎总是有很多方法可以得到相同的结果。

    cat接受来自参数的文件,或者stdin如果没有参数。

    见man cat:

    SYNOPSIS
           cat [OPTION]... [FILE]...
    
    DESCRIPTION
           Concatenate FILE(s) to standard output.
    
           With no FILE, or when FILE is -, read standard input.
    
    • 17
  3. WinEunuuchs2Unix
    2019-11-22T16:03:29+08:002019-11-22T16:03:29+08:00

    一大区别

    一个很大的区别是*,?或[globbing 字符(通配符)或任何其他 shell 可能扩展为多个文件名的内容。任何外壳扩展为两个或多个项目,而不是视为单个文件名,都不能打开以进行重定向。

    如果没有重定向(即 no <),shell 会将多个文件名传递给cat,它会一个接一个地输出文件的内容。例如这有效:

    $ ls hello?.py
    hello1.py  hello2.py
    
    $ cat hello?.py
    
    # Output for two files 'hello1.py' and 'hello2.py' appear on your screen
    

    但是使用重定向 ( <) 会出现错误消息:

    $ ls < hello?.py
    bash: hello?.py: ambiguous redirect
    
    $ cat < hello?.py
    bash: hello?.py: ambiguous redirect
    

    一个微小的差异

    我认为重定向会更慢,但没有可察觉的时间差异:

    $ time for f in * ; do cat "$f" > /dev/null ; done
    
    real    0m3.399s
    user    0m0.130s
    sys     0m1.940s
    
    $ time for f in * ; do cat < "$f" > /dev/null ; done
    
    real    0m3.430s
    user    0m0.100s
    sys     0m2.043s
    

    笔记:

    • 在此测试中,差异约为 1/1000(千分之一)秒。在其他测试中,它是 1/100 秒,仍然无法注意到。
    • 交替测试几次,以便尽可能多地将数据缓存到 RAM 中,并返回更一致的比较时间。另一种选择是在每次测试之前删除所有缓存。
    • 14
  4. user1018475
    2019-11-23T08:19:56+08:002019-11-23T08:19:56+08:00

    主要区别在于谁打开文件、shell 或 cat。他们可能在不同的许可制度下运行,所以

    sudo cat /proc/some-protected-file
    

    可能会工作,而

    sudo cat < /proc/some-protected-file
    

    将失败。当只是想用于简单的脚本编写时,这种权限机制可能有点难以解决echo,因此存在滥用的tee权宜之计

    echo level 7|sudo tee /proc/acpi/ibm/fan
    

    由于权限问题,这实际上并不能使用重定向来代替。

    • 10
  5. Sergiy Kolodyazhnyy
    2019-11-30T02:36:38+08:002019-11-30T02:36:38+08:00

    TL;DR 答案版本:

    • 应用cat file.txt程序(在本例中cat)接收到一个位置参数,对其执行 open(2) 系统调用,并在应用程序内进行权限检查。

    • 使用cat < file.txtshell 将执行dup2()系统调用以使stdin 成为与该文件描述符(例如3)相对应的文件描述符(通常是下一个可用的,例如3)的副本,file.txt并关闭该文件描述符(例如3)。应用程序不对文件执行 open(2) 并且不知道文件的存在;它严格按照其标准输入文件描述符运行。权限检查取决于外壳。打开文件的描述将与外壳打开文件时保持相同。

    介绍

    从表面上看cat file.txt,cat < file.txt行为相同,但在幕后还有更多的事情发生,只有一个性格差异。这个<字符会改变 shell 的理解方式file.txt、打开文件的人以及文件在 shell 和命令之间传递的方式。当然,为了解释所有这些细节,我们还需要了解如何在 shell 中打开文件和运行命令,这就是我的答案旨在实现的目标 - 用最简单的术语教育读者这些看似简单的命令。在这个答案中,您会找到多个示例,包括那些使用strace命令备份幕后实际发生的解释的示例。

    由于 shell 和命令的内部工作方式是基于标准系统调用的,cat因此仅将其视为许多其他命令中的一个很重要。如果您是阅读此答案的初学者,请保持开放的心态,并注意这prog file.txt并不总是与prog < file.txt. 当两种形式应用于不同的命令时,它的行为可能完全不同,这取决于权限或程序的编写方式。我还请您暂停判断,并从不同用户的角度看待这一点——对于一个临时的 shell 用户来说,他们的需求可能与系统管理员和开发人员的需求完全不同。

    execve() 系统调用和可执行文件看到的位置参数

    Shell 通过使用fork(2) syscall创建一个子进程并调用execve(2) syscall 来运行命令,后者使用指定的参数和环境变量执行命令。内部调用的命令execve()将接管并替换进程;例如,当 shell 调用cat时,它将首先创建一个 PID 为 12345 的子进程,然后execve()PID 12345 变为cat.

    cat file.txt这给我们带来了和之间的区别cat < file.txt。在第一种情况下,cat file.txt是使用一个位置参数调用的命令,shell 将execve()适当地组合在一起:

    $ strace -e execve cat testfile.txt
    execve("/bin/cat", ["cat", "testfile.txt"], 0x7ffcc6ee95f8 /* 50 vars */) = 0
    hello, I am testfile.txt
    +++ exited with 0 +++
    

    在第二种情况下,该<部分是 shell 运算符,并< testfile.txt告诉 shell 打开testfile.txt并将 stdin 文件描述符 0 转换为文件描述符的副本,该副本对应于testfile.txt. 这意味着< testfile.txt不会作为位置参数传递给命令本身:

    $ strace -e execve cat < testfile.txt
    execve("/bin/cat", ["cat"], 0x7ffc6adb5490 /* 50 vars */) = 0
    hello, I am testfile.txt
    +++ exited with 0 +++
    $ 
    

    如果程序需要位置参数才能正常运行,这可能很重要。在这种情况下,cat如果没有提供与文件对应的位置参数,则默认接受来自标准输入的输入。这也将我们带到了下一个主题:标准输入和文件描述符。

    STDIN 和文件描述符

    谁打开文件cat或外壳?他们如何打开它?他们甚至有权打开它吗?这些是可以提出的问题,但首先我们需要了解打开文件的工作原理。

    当一个进程执行open()或openat()处理一个文件时,这些函数为进程提供一个与打开的文件相对应的整数,然后程序可以通过引用该整数来调用read()、seek()、 和调用以及无数其他系统调用。write()当然,系统(又名内核)将在内存中保存特定文件的打开方式、权限、模式 - 只读、只写、读/写 - 以及我们当前在文件中的位置- 在字节 0 或字节 1024 - 这称为偏移量。这称为打开文件描述。

    在最基本的层面上,cat testfile.txt是cat打开文件的位置,它将被下一个可用的文件描述符引用,即 3(注意read(2)中的 3 )。

    $ strace -e read -f cat testfile.txt > /dev/null
    ...
    read(3, "hello, I am testfile.txt and thi"..., 131072) = 79
    read(3, "", 131072)                     = 0
    +++ exited with 0 +++
    

    相比之下,cat < testfile.txt将使用文件描述符 0(又名 stdin):

    $ strace -e read -f cat < testfile.txt > /dev/null
    ...
    read(0, "hello, I am testfile.txt and thi"..., 131072) = 79
    read(0, "", 131072)                     = 0
    +++ exited with 0 +++
    

    还记得早些时候我们了解到 shellfork()先通过然后exec() 类型的进程运行命令吗?好吧,事实证明文件是如何打开的,从而转移到使用fork()/exec()模式创建的子进程。引用open(2) 手册:

    当一个文件描述符被复制时(使用 dup(2) 或类似的方法),副本指的是与原始文件描述符相同的打开文件描述,因此两个文件描述符共享文件偏移量和文件状态标志。 这种共享也可能发生在进程之间:通过 fork(2) 创建的子进程继承其父文件描述符的副本,这些副本引用相同的打开文件描述

    这对cat file.txtvs意味着什么cat < file.txt?其实很多。在cat file.txt打开cat文件时,这意味着它可以控制文件的打开方式。在第二种情况下,shell 将打开file.txt它,并且对于子进程、复合命令和管道,它的打开方式将保持不变。我们目前在文件中的位置也将保持不变。

    我们以这个文件为例:

    $ cat testfile.txt 
    hello, I am testfile.txt and this is first line
    line two
    line three
    
    last line
    

    看下面的例子。为什么line第一行的单词没有变化?

    $ { head -n1; sed 's/line/potato/'; }  <  testfile.txt 2>/dev/null
    hello, I am testfile.txt and this is first line
    potato two
    potato three
    
    last potato
    

    答案就在上面open(2)手册的引用中:shell 打开的文件被复制到复合命令的标准输入上,并且每个运行的命令/进程共享打开文件描述的偏移量。head只需将文件向前倒退一行,然后sed处理其余部分。更具体地说,我们会看到 2 个dup2()/ fork()/execve()系统调用序列,并且在每种情况下,我们都会获得文件描述符的副本,它在 open 上引用相同的文件描述testfile.txt。使困惑 ?让我们举一个更疯狂的例子:

    $ { head -n1; dd of=/dev/null bs=1 count=5; cat; }  <  testfile.txt 2>/dev/null
    hello, I am testfile.txt and this is first line
    two
    line three
    
    last line
    

    在这里,我们打印了第一行,然后将打开的文件描述倒回前 5 个字节(消除了单词line),然后只打印了其余部分。我们是如何做到的?打开的文件描述testfile.txt保持不变,文件上的共享偏移量。

    现在,除了编写像上面这样疯狂的复合命令之外,为什么这有助于理解?作为开发人员,您可能希望利用或提防此类行为。假设cat您编写了一个需要作为文件传递或从标准输入传递的配置的 C 程序,而您像myprog myconfig.json. 如果你跑了会发生什么{ head -n1; myprog;} < myconfig.json?最好的情况是你的程序会得到不完整的配置数据,最坏的情况是——破坏程序。我们还可以利用它作为产生子进程的优势,并让父进程回退到子进程应该处理的数据。

    权限和特权

    这次让我们从一个对其他用户没有读取或写入权限的文件的示例开始:

    $ sudo -u potato cat < testfile.txt
    hello, I am testfile.txt and this is first line
    line two
    line three
    
    last line
    $ sudo -u potato cat testfile.txt
    cat: testfile.txt: Permission denied
    

    这里发生了什么 ?为什么我们可以在第一个示例中以potato用户身份读取文件,但不能在第二个示例中读取文件?这可以追溯到前面提到的open(2)手册页中的相同引用。使用< file.txtshell 打开文件,因此权限检查发生在shell 执行open/openat()时。当时的 shell 以对文件具有读取权限的文件所有者的权限运行。由于跨dup2调用继承了打开文件描述,shell 将打开文件描述符的副本 sudo传递给 ,将文件描述符的副本传递给cat,并且cat不知道其他任何内容,因此愉快地读取文件的内容。在最后一个命令中,cat土豆下的用户执行open()在文件上,当然该用户没有读取文件的权限。

    更实际和更常见的是,这就是为什么用户对为什么这样的东西不起作用(运行特权命令写入他们无法打开的文件)感到困惑:

    $ sudo echo 100 > /sys/class/drm/*/intel_backlight/brightness
    bash: /sys/class/drm/card0-eDP-1/intel_backlight/brightness: Permission denied
    

    但是这样的事情是有效的(使用特权命令写入需要特权的文件):

    $ echo 100 |sudo tee /sys/class/drm/*/intel_backlight/brightness
    [sudo] password for administrator: 
    100
    

    privileged_prog < file.txt与我之前展示的情况(失败但确实有效)相反的情况的理论示例privileged_prog file.txt是使用 SUID 程序。SUID 程序,例如passwd,允许执行具有可执行所有者权限的操作。这就是为什么passwd命令允许您更改密码,然后将该更改写入/etc/shadow,即使该文件由 root 用户拥有。

    为了示例和乐趣,我实际上用catC 语言编写了类似演示的应用程序(此处为源代码)并设置了 SUID 位,但如果你明白了 - 随时跳到这个答案的下一部分并忽略这部分. 旁注:操作系统忽略解释可执行文件上的 SUID 位#!,因此同一事物的 Python 版本会失败。

    让我们检查程序的权限和testfile.txt:

    $ ls -l ./privileged
    -rwsr-xr-x 1 administrator administrator 8672 Nov 29 16:39 ./privileged
    $ ls -l testfile.txt
    -rw-r----- 1 administrator administrator 79 Nov 29 12:34 testfile.txt
    

    看起来不错,只有文件所有者和属于administrator组的人才能读取此文件。现在让我们以土豆用户身份登录并尝试读取文件:

    $ su potato
    Password: 
    potato@my-PC:/home/administrator$ cat ./testfile.txt
    cat: ./testfile.txt: Permission denied
    potato@my-PC:/home/administrator$ cat  < ./testfile.txt
    bash: ./testfile.txt: Permission denied
    

    看起来不错,shell 和cat拥有土豆用户权限的人都不能读取他们不允许读取的文件。还要注意谁报告了错误 - catvs bash。让我们测试一下我们的 SUID 程序:

    potato@my-PC:/home/administrator$ ./privileged testfile.txt
    hello, I am testfile.txt and this is first line
    line two
    line three
    
    last line
    potato@my-PC:/home/administrator$ ./privileged < testfile.txt
    bash: testfile.txt: Permission denied
    

    按预期工作!同样,这个小演示的要点是,prog file.txt打开prog < file.txt文件的人不同,打开文件的权限也不同。

    程序如何对 STDIN 做出反应

    我们已经知道< testfile.txt以这样的方式重写标准输入,即数据将来自指定的文件而不是键盘。理论上,并且基于 Unix 的“做一件事并把它做好”的哲学,从标准输入(又名文件描述符 0)读取的程序应该表现一致,因此prog1 | prog2应该类似于prog2 file.txt. 但是,如果prog2想用lseek系统调用倒带,例如为了跳到某个字节或倒带到最后以找出我们有多少数据,该怎么办?

    某些程序不允许从管道读取数据,因为管道无法使用lseek(2)系统调用倒带,或者数据无法使用mmap(2)加载到内存中以加快处理速度。Stephane Chazelas在这个问题中 的出色回答已经涵盖了这一点: “cat file | ./binary”和“./binary < 文件”?我强烈推荐阅读。

    幸运的是,cat < file.txt它的cat file.txt行为始终如一,并且cat不以任何方式反对管道,尽管我们知道它读取完全不同的文件描述符。prog file.txt这在一般情况下如何应用prog < file.txt?如果一个程序真的不想对管道做任何事情,缺少位置参数file.txt就足以出错退出,但应用程序仍然可以在标准输入上使用lseek()来检查它是否是管道(尽管isatty(3)或检测fstat (2)中的S_ISFIFO 模式更有可能用于检测管道输入),在这种情况下,做类似./binary <(grep pattern file.txt) 或./binary < <(grep pattern file.txt)可能不起作用的事情。

    文件类型影响

    文件类型可能会影响prog filevsprog < file行为。这在某种程度上意味着作为程序的用户,即使您不知道这样做,您也在选择系统调用。例如,假设我们有一个 Unix 域套接字并且我们运行nc服务器来监听它,也许我们甚至准备了一些要服务的数据

    $ nc -U -l /tmp/mysocket.sock   < testfile.txt 
    

    在这种情况下,/tmp/mysocket.sock将通过不同的系统调用打开:

    socket(AF_UNIX, SOCK_STREAM, 0)         = 3
    setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
    bind(3, {sa_family=AF_UNIX, sun_path="/tmp/mysocket.sock"}, 20) = 0
    

    现在,让我们尝试从不同终端中的该套接字读取数据:

    $ cat /tmp/mysocket.sock
    cat: /tmp/mysocket.sock: No such device or address
    $ cat <  /tmp/mysocket.sock
    bash: /tmp/mysocket.sock: No such device or address
    

    shell 和 cat 都open(2)在对需要完全不同的系统调用的东西执行系统调用——socket(2) 和 connect(2) 对。即使这样也行不通:

    $ nc -U  < /tmp/mysocket.sock
    bash: /tmp/mysocket.sock: No such device or address
    

    但是,如果我们知道文件类型以及如何调用正确的系统调用,我们可以获得所需的行为:

    $ nc -U /tmp/mysocket.sock
    hello, I am testfile.txt and this is first line
    line two
    line three
    
    last line
    

    注释和其他建议阅读:

    • open(2)手册中的引用指出文件描述符的权限被继承。理论上,有一种方法可以更改文件描述符的读/写权限,但这必须在源代码级别完成。

    • 什么是打开文件描述?. 另见POSIX 定义

    • Linux如何检查文件描述符的权限?

    • 为什么 的 行为command 1>file.txt 2>file.txt不同于command 1>file.txt 2>&1?

    • 9

相关问题

  • 如何从命令行仅安装安全更新?关于如何管理更新的一些提示

  • 如何从命令行刻录双层 dvd iso

  • 如何从命令行判断机器是否需要重新启动?

  • 文件权限如何工作?文件权限用户和组

  • 如何在 Vim 中启用全彩支持?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    如何运行 .sh 脚本?

    • 16 个回答
  • Marko Smith

    如何安装 .tar.gz(或 .tar.bz2)文件?

    • 14 个回答
  • Marko Smith

    如何列出所有已安装的软件包

    • 24 个回答
  • Marko Smith

    无法锁定管理目录 (/var/lib/dpkg/) 是另一个进程在使用它吗?

    • 25 个回答
  • Martin Hope
    Flimm 如何在没有 sudo 的情况下使用 docker? 2014-06-07 00:17:43 +0800 CST
  • Martin Hope
    Ivan 如何列出所有已安装的软件包 2010-12-17 18:08:49 +0800 CST
  • Martin Hope
    La Ode Adam Saputra 无法锁定管理目录 (/var/lib/dpkg/) 是另一个进程在使用它吗? 2010-11-30 18:12:48 +0800 CST
  • Martin Hope
    David Barry 如何从命令行确定目录(文件夹)的总大小? 2010-08-06 10:20:23 +0800 CST
  • Martin Hope
    jfoucher “以下软件包已被保留:”为什么以及如何解决? 2010-08-01 13:59:22 +0800 CST
  • Martin Hope
    David Ashford 如何删除 PPA? 2010-07-30 01:09:42 +0800 CST

热门标签

10.10 10.04 gnome networking server command-line package-management software-recommendation sound xorg

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve