DownloadManager 下载管理类

演示





 


简介


从Android 2.3开始新增了一个下载管理类,在SDK的文档中我们查找android.app.DownloadManager可以看到。下载管理类可以长期处理多个HTTP下载任务,客户端只需要给出请求的Uri和存放目标文件的位置即可,下载管理使用了一个AIDL服务器,所以可以放心的在后台执行,同时实例化的方法需要使用getSystemService(Context.DOWNLOAD_SERVICE) ,我们可以轻松的通过新增的这个API实现Android平台上的文件下载操作。

DownloadManager类提供了以下几种方法来处理, long  enqueue(DownloadManager.Request request)   //存入队列一个新的下载项 ParcelFileDescriptor  openDownloadedFile(long id)  //打开一个下载后的文件用于读取,参数中的long型id是一个provider中的一条记录 Cursor  query(DownloadManager.Query query)  //查询一个下载,返回一个Cursor int  remove(long... ids)  //取消下载,同时从下载管理中移除这些条   我们可以看到提供的方法都比较简单,给我们操作的最终封装成为一个provider数据库的方式进行添加、查询和移除,但是对于查询和添加任务的细节,我们要看看DownloadManager.Request类和DownloadManager.Query 类了。
DownloadManager.Request类 addRequestHeader(String header, String value)  // 添加一个Http请求报头,比如说User-Agent值可以为Android123或Windows XP等等了,主要是给服务器提供标识 setAllowedNetworkTypes(int flags)  //设置允许使用的网络类型,目前仅有两种定义,分别为NETWORK_MOBILE(移动网络)和NETWORK_WIFI,可以使用"|"运算 setAllowedOverMetered(boolean allow)  //是否允许“计量式的网络连接”执行下载操作,默认是true允许的 setAllowedOverRoaming(boolean allowed)  //是否允许使用漫游,默认是true允许的 setDescription(CharSequence description)  //设置Notification的描述信息 setDestinationInExternalFilesDir(Context context, String dirType, String subPath)  //设置将文件存储在【data/data/包名/files】目录;第2个参数是files目录下新建目录的目录名,不存在会自己创建;第3个参数是文件名,如果第3个参数带路径,要确保路径存在;不设置会存在【data/data/com.Android.provider.downloads/cache/】下面 setDestinationInExternalPublicDir(String dirType, String subPath)  //设置将文件存储在sd卡目录 setDestinationUri(Uri uri)  //设置需要下载目标的Uri,可以是http、ftp等等了 setMimeType(String mimeType)  //设置mime类型,这里看服务器配置,一般国家化的都为utf-8编码 setShowRunningNotification(boolean show)  //是否显示下载进度的提示,设为false需要权限【DOWNLOAD_WITHOUT_NOTIFICATION】,已经被废弃了 setNotificationVisibility(int visibility)  //设置Notification的显示和隐藏。取值:DownloadManager.Request.VISIBILITY_VISIBLE(默认值,在下载任务执行的过程中显示,下载完成后自动消失),_VISIBLE_NOTIFY_COMPLETED(下载进行时和完成之后都会显示),_HIDDEN(权限),_VISIBLE_NOTIFY_ONLY_COMPLETION(只有当任务完成时才会显示) setTitle(CharSequence title)  //设置Notification的标题 setVisibleInDownloadsUi(boolean isVisible)  //设置下载管理类在处理过程中的界面是否显示。Set whether this download should be displayed in the system‘s Downloads UI. True by default Google还提供了一个简单的方法来实例化本类,这个构造方法为DownloadManager.Request(Uri uri) ,我们直接填写一个Uri即可,上面的设置使用默认情况。
DownloadManager.Query类 对于当前下载内容的状态,我们可以使用DownloadManager.Query类来获取,本类比较简单,仅仅提供了两个方法。 setFilterById(long... ids)  //根据id来过滤查找 setFilterByStatus(int flags) //根据任务的状态来查找  

定义的常量


下载的状态完成均是以广播的形式通知大家,目前定义了下面三种Action:

ACTION_DOWNLOAD_COMPLETE//下载完成的动作

ACTION_NOTIFICATION_CLICKED //当用户单击notification中下载管理的某项时触发

ACTION_VIEW_DOWNLOADS //查看下载项

设置Notification的显示和隐藏: DownloadManager.Request.VISIBILITY_VISIBLE//默认值,在下载任务执行的过程中显示,下载完成后自动消失 DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED//下载进行时和完成之后都会显示 DownloadManager.Request.VISIBILITY_HIDDEN//将不会显示,如果设置该属性的话,必须添加权限android.permission.DOWNLOAD_WITHOUT_NOTIFICATION DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION//只有当任务完成时才会显示  

DownloadManager类提供的query方法返回一个Cursor对象,其中定义的属性有以下几种:



任务目前的状态保存在这个游标的COLUMN_STATUS 字段中,任务的状态有:

STATUS_FAILED  //失败

STATUS_PAUSED  //暂停

STATUS_PENDING  //等待将开始

STATUS_RUNNING  //正在处理中

STATUS_SUCCESSFUL  //已经下载成功

对于一个尚未完成的项,在Cursor中我们查找COLUMN_REASON字段,可能是以下定义:

ERROR_CANNOT_RESUME  //不能够继续,由于一些其他原因

ERROR_DEVICE_NOT_FOUND  //外部存储设备没有找到,比如SD卡没有插入

ERROR_FILE_ALREADY_EXISTS  //要下载的文件已经存在了,下载管理类是不会覆盖已经存在的文件,所以如果需要重新下载,请先删除以前的文件

ERROR_FILE_ERROR  //可能由于SD卡原因导致了文件错误

ERROR_HTTP_DATA_ERROR  //在Http传输过程中出现了问题

ERROR_INSUFFICIENT_SPACE  //由于SD卡空间不足造成的

ERROR_TOO_MANY_REDIRECTS  //这个Http有太多的重定向,导致无法正常下载

ERROR_UNHANDLED_HTTP_CODE  //无法获取http出错的原因,比如说远程服务器没有响应

ERROR_UNKNOWN  //未知的错误类型



有关暂停的一些状态,同样COLUMN_REASON字段的值,可能是以下定义:

PAUSED_QUEUED_FOR_WIFI  //由于移动网络数据问题,等待WiFi连接能用后再重新进入下载队列。

PAUSED_UNKNOWN  //未知原因导致了任务下载的暂停.

PAUSED_WAITING_FOR_NETWORK  //可能由于没有网络连接而无法下载,等待有可用的网络连接恢复。.

PAUSED_WAITING_TO_RETRY  //由于重重原因导致下载暂停,等待重试。

关于下载目录


指定下载位置,及文件名称

1、request.setDestinationInExternalFilesDir(context,"TestDownload","Test.apk")

官方说明:Set the local destination for the downloaded file to a path within the application‘s external files directory (as returned by getExternalFilesDir(String).

翻译:给下载文件 “制定” 一个路径,文件路径的“特性”跟 getExternalFilesDir(String)类似。

关于getExternalFilesDir(String):

这个方法的返回值是一个文件夹,这个文件夹是被创建在【/data/data/包名/files/】目录下的,他一般是用来存储你的app运行所需的文件的(如图片的缓存)。默认情况下这个文件夹只有当前app有访问权限,当你的应用程序被卸载之后这个文件夹中的数据也会被清除。

2、request.setDestinationInExternalPublicDir("TestDownload","Test.apk"); 官方说明:Set the local destination for the downloaded file to a path within the public external storage directory (as returned by getExternalStoragePublicDirectory(String)). 翻译:这个方法也是用来“制定”一个路径的,这个路径的特性类似于getExternalStoragePublicDirectory(String)) 关于getExternalStoragePublicDirectory(String): 这个方法的返回值是一个文件夹,这个文件夹是被创建在你的SD卡根目录的【mnt/sdcard/】 这个文件夹中的内容其他程序都是可以访问的,当你的应用程序被卸载的时候,这个文件夹中的内容不会丢失。
通过Context.getExternalFilesDir()方法可以获取到 SDCard/Android/data/你的应用的包名/files/ 目录,一般放一些长时间保存的数据 通过Context.getExternalCacheDir()方法可以获取到 SDCard/Android/data/你的应用包名/cache/目录,一般存放临时缓存数据 如果使用上面的方法,当你的应用在被用户卸载后,SDCard/Android/data/你的应用的包名/ 这个目录下的所有文件都会被删除,不会留下垃圾信息。 而且上面二个目录分别对应 设置->应用->应用详情里面的”清除数据“与”清除缓存“选项 如果要保存下载的内容,就不要放在以上目录下

Activity


public class DownloadManagerActivity extends ListActivity {

    public static final String URL_SMALL_FILE = "http://f2.market.xiaomi.com/download/AppStore/0b6c25446ea80095219649f646b8d67361b431127/com.wqk.wqk.apk";

    public static final String URL_BIG_FILE = "http://f3.market.xiaomi.com/download/AppChannel/099d2b4f6006a4c883059f459e0025a3e1f25454e/com.pokercity.bydrqp.mi.apk";

    private DownloadCompleteReceiver receiver;

    private TextView tv_info;

    private int status = 1;

    public static final int MSG_WHAT_DOWNLOAD_ID = 1;

    private boolean isQueryDownTaskById = false;

    @SuppressLint("HandlerLeak")

    private Handler mHandler = new Handler() {

        @Override

        public void handleMessage(Message msg) {

            switch (msg.what) {

            case MSG_WHAT_DOWNLOAD_ID:

                if (isQueryDownTaskById) {

                    long id = (long) msg.obj;

                    tv_info.append("查询结果\n");

                    Map<String, String> map = queryDownTaskById(DownloadManagerActivity.this, id);

                    for (Map.Entry<String, String> k_v : map.entrySet()) {//增强for遍历键值对

                        tv_info.append(k_v + "\n");

                    }

                }

                break;

            }

        };

    };

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        String[] array = { "注册接收DownloadManager三个广播", "取消注册广播接收者", //

                "下载文件到SD卡下的Download目录", "下载文件到SD卡指定目录", "下载文件到/data/data/包名/files/目录", //

                "通过状态查询下载任务", "通过id查询下载任务", };

        for (int i = 0; i < array.length; i++) {

            array[i] = i + "、" + array[i];

        }

        tv_info = new TextView(this);// 将内容显示在TextView中

        tv_info.setTextColor(Color.BLUE);

        tv_info.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);

        tv_info.setPadding(20, 10, 20, 10);

        getListView().addFooterView(tv_info);

        setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, new ArrayList<String>(Arrays.asList(array))));

        //加入到ThreadLocal中

        HandlerManager.setHandler(mHandler);

    }

    @Override

    protected void onListItemClick(ListView l, View v, int position, long id) {

        switch (position) {

        case 0:

            if (receiver == null) receiver = new DownloadCompleteReceiver();

            IntentFilter intentFilter = new IntentFilter();

            intentFilter.addAction(DownloadManager.ACTION_DOWNLOAD_COMPLETE);//下载完成的动作

            intentFilter.addAction(DownloadManager.ACTION_NOTIFICATION_CLICKED);//当用户单击notification中下载管理的某项时触发

            intentFilter.addAction(DownloadManager.ACTION_VIEW_DOWNLOADS);//查看下载项

            registerReceiver(receiver, intentFilter);

            break;

        case 1:

            if (receiver != null) {

                unregisterReceiver(receiver);

                receiver = null;

            }

            break;

        case 2:

            simpleDownLoadFileToSdNoUI(this, URL_SMALL_FILE);

            break;

        case 3:

            downLoadFile(this, URL_BIG_FILE, "包青天", new Random().nextInt(1000) + ".apk", DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED, //

                    DownloadManager.Request.NETWORK_WIFI | DownloadManager.Request.NETWORK_MOBILE, true);

            break;

        case 4:

            simpleDownLoadFileToDataNoUI(this, URL_SMALL_FILE);

            break;

        case 5:

            tv_info.setText("\n" + getStatusString(status) + "    的任务有:");

            List<Map<String, String>> runningList = queryDownTaskByStatus(this, status);//共有五种状态

            // 遍历

            for (Map<String, String> hm : runningList) {

                tv_info.append("\n**********************************");

                Set<String> set = hm.keySet();

                for (String key : set) {

                    String value = hm.get(key);

                    tv_info.append("\n" + key + "-" + value);

                }

            }

            //下一个状态

            status = status << 1;

            if (status > (1 << 4)) status = 1;

            break;

        case 6:

            isQueryDownTaskById = true;

            break;

        }

    }

    public static String getStatusString(int status) {

        switch (status) {

        case DownloadManager.STATUS_FAILED:

            return "失败";

        case DownloadManager.STATUS_PAUSED:

            return "暂停";

        case DownloadManager.STATUS_PENDING:

            return "等待";

        case DownloadManager.STATUS_RUNNING:

            return "正在下载";

        case DownloadManager.STATUS_SUCCESSFUL:

            return "下载成功";

        default:

            return "没有这种状态";

        }

    }

    public static void simpleDownLoadFileToSdNoUI(Context mContext, String url) {

        downLoadFile(mContext, url, null, null, DownloadManager.Request.VISIBILITY_HIDDEN, DownloadManager.Request.NETWORK_WIFI, true);

    }

    public static void simpleDownLoadFileToDataNoUI(Context mContext, String url) {

        downLoadFile(mContext, url, null, null, DownloadManager.Request.VISIBILITY_HIDDEN, DownloadManager.Request.NETWORK_WIFI, false);

    }

    /**

     * 使用DownloadManager下载文件

     * @param mContext    上下文

     * @param url                文件路径,请自行确保路径正确

     * @param filePath        子目录名,设为null则不创建子目录

     * @param fileName    文件名,设为null则使用服务器路径中的文件名

     * @param visibility        通知显示的类型,请使用DownloadManager.Request中定义的常量,设置为不显示需要权限DOWNLOAD_WITHOUT_NOTIFICATION

     * @param networkType    只允许在指定的网络类型下下周,请使用DownloadManager.Request中定义的常量,可以使用"|"运算

     * @param isToSdCard    是否保存到SD卡上,设为false则保存到【/data/data/包名/files/】目录下

     */

    public static void downLoadFile(Context mContext, String url, String filePath, String fileName, int visibility, int networkType, boolean isToSdCard) {

        if (url == null || url == "") url = "http://www.sinaimg.cn/dy/slidenews/3_img/2016_22/77542_379697_224394.jpg";

        if (filePath == null || filePath == "") filePath = Environment.DIRECTORY_DOWNLOADS;

        if (fileName == null || fileName == "") fileName = url.substring(url.lastIndexOf("/") + 1);//截取文件名及后缀名

        if (fileName == null || fileName == "") fileName = new SimpleDateFormat("yyyy.MM.dd HH-mm-ss", Locale.CHINA).format(new Date());//截取失败时自动命名

        DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url)).setNotificationVisibility(visibility).setAllowedNetworkTypes(networkType);

        if (isToSdCard) request.setDestinationInExternalPublicDir(filePath, fileName);//以SD卡路径为根路径

        else request.setDestinationInExternalFilesDir(mContext, filePath, fileName);//以【/data/data/包名/files/】为根路径

        ((DownloadManager) mContext.getSystemService(DOWNLOAD_SERVICE)).enqueue(request);//将下载请求放入队列

    }

    public static List<Map<String, String>> queryDownTaskByStatus(Context mContext, int status) {

        DownloadManager.Query query = new DownloadManager.Query();

        query.setFilterByStatus(status);

        Cursor cursor = ((DownloadManager) mContext.getSystemService(DOWNLOAD_SERVICE)).query(query);

        if (cursor != null) {

            List<Map<String, String>> data = new ArrayList<Map<String, String>>();

            while (cursor.moveToNext()) {

                String id = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_ID));

                String title = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_TITLE));

                String uri = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));

                String name = cursor.getString(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_FILENAME));

                //String mStatu = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));

                String sizeNow = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));//已下载

                String sizeTotal = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));

                Map<String, String> map = new HashMap<String, String>();

                map.put("id", id);

                map.put("title", title);

                map.put("name", name);

                map.put("uri", uri);

                map.put("status", sizeTotal + ":" + sizeNow);

                data.add(map);

            }

            cursor.close();

            return data;

        }

        return null;

    }

    public static Map<String, String> queryDownTaskById(Context mContext, long id) {

        DownloadManager.Query query = new DownloadManager.Query();

        query.setFilterById(id);

        Cursor cursor = ((DownloadManager) mContext.getSystemService(DOWNLOAD_SERVICE)).query(query);

        if (cursor != null) {

            Map<String, String> data = new HashMap<String, String>();

            if (cursor.moveToNext()) {

                String sizeNow = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));

                String sizeTotal = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));

                data.put("sizeNow", sizeNow);

                data.put("sizeTotal", sizeTotal);

            }

            cursor.close();

            return data;

        }

        return null;

    }

}

广播


/** 注册一个广播接收器,当下载完毕后会收到一个android.intent.action.DOWNLOAD_COMPLETE的广播,在这里取出队列里下载任务,进行安装*/

public class DownloadCompleteReceiver extends BroadcastReceiver {

    @Override

    public void onReceive(Context context, Intent intent) {

        long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0);

        Handler mHandler = HandlerManager.getHandler();

        mHandler.sendMessage(Message.obtain(mHandler, DownloadManagerActivity.MSG_WHAT_DOWNLOAD_ID, id));

        if (intent.getAction().equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {

            Toast.makeText(context, "编号 " + id + " 的下载任务已经完成!", Toast.LENGTH_SHORT).show();

        } else if (intent.getAction().equals(DownloadManager.ACTION_NOTIFICATION_CLICKED)) {

            Toast.makeText(context, "别瞎点-" + id, Toast.LENGTH_SHORT).show();

        } else if (intent.getAction().equals(DownloadManager.ACTION_VIEW_DOWNLOADS)) {

            Toast.makeText(context, "查看下载项-" + id, Toast.LENGTH_SHORT).show();

        }

        //如果下载的是APK文件,则自动安装

        DownloadManager downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);

        Query query = new DownloadManager.Query();

        query.setFilterById(id);

        Cursor cursor = downloadManager.query(query);

        if (cursor != null) {

            if (cursor.moveToFirst()) {

                int status = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS));

                String name = cursor.getString(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_FILENAME));

                String uri = cursor.getString(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_URI));

                String end = name.substring(name.lastIndexOf("."));

                if (end != null && end.equals(".apk") && status == DownloadManager.STATUS_SUCCESSFUL) {

                    Intent mIntent = new Intent(Intent.ACTION_VIEW);

                    mIntent.setDataAndType(Uri.parse(uri), "application/vnd.android.package-archive");

                    mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

                    context.startActivity(mIntent);

                }

            }

            cursor.close();

        }

    }

}

UI线程共享的Handler


/**在UI线程中均可以使用此Handler,如Activity、Service、BroadcastReceiver,他们用的都是同一个对象,可以方便在相互通讯

 * ThreadLocal会为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

 */

public class HandlerManager {

    private static ThreadLocal<Handler> threadLocal = new ThreadLocal<Handler>();

    public static Handler getHandler() {

        return threadLocal.get();//返回当前线程所对应的线程局部变量

    }

    public static void setHandler(Handler mHandler) {

        threadLocal.set(mHandler);//设置当前线程的线程局部变量的值

    }

}

来自为知笔记(Wiz)

时间: 2024-10-12 17:40:40

DownloadManager 下载管理类的相关文章

android webview downloadManager文件下载管理

一.downloadmanager类说明: 从Android 2.3开始新增了一个下载管理类,在SDK的文档中我们查找android.app.DownloadManager可以看到.下载管理类可以长期处理多个HTTP下载任务,客户端只需要给出请求的Uri和存放目标文件的位置即可,下载管理使用了一个AIDL服务器所以可以放心的在后台执行,同时实例化的方法需要使用getSystemService(Context.DOWNLOAD_SERVICE) ,Android123再次提醒使用API Level

Android系统下载管理DownloadManager功能介绍及使用示例

http://www.trinea.cn/android/android-downloadmanager/ 本文主要结合源码介绍Android系统下载管理DownloadManager的强大功能及使用.这是许久来准备写的一系列博客,这篇主要介绍DownloadManager的功能和示例,后面还有两篇会介绍下载管理的底层设计(DownloadProvider.DownloadManager.DownloadManagerUI).下载管理如何进行功能增强和bug修改. 示例APK可从这些地址下载:G

Android下载管理DownloadManager功能扩展和bug修改

http://www.trinea.cn/android/android-downloadmanager-pro/ 本文主要介绍如何修改Android系统下载管理,以支持更多的功能及部分bug修改和如何编译生效.目前内容包括暂停下载.继续下载.通知设置NotiExtra和NotiClass.wifi切换到3g自动暂停.Bug修改. PS: 很多童鞋不是自己做rom,所以就算修改了系统源码编译出来的包在其他系统上也不通用这里推荐[email protected](并不是我的开源项目,我的项目为[e

下载的管理类MyDownloadManager

import android.content.Intent; import android.net.Uri; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; /** * 下载的管理类 */ public class MyDownloadManager { public sta

线程池的管理类MyThreadPoolManager

import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * 线程池的管理类,单例 */ public class MyThre

玩转linux第四天之用户及组管理类相关命令(二)

1    前言 本文档主要介绍用户及组管理类相关命令如:useradd .usermod.passwd.userdel.groupadd.groupmod\gpasswd.groupdel.hage,chsh,chfn,.id,w,who,whoami.wck,gourpck.su,并提供相应案例解释其含义. 学习这些基础命令是一个Linux爱好者需要具备的首要条件,让小编带领你们进入Linux的世界看看它的一颦一笑. 读者对象 本文档(本指南)主要适用于以下人员: Linux爱好者 2    

定制自己的线程池管理类、防止OOM

在Android开发中.涉及比较深的话.我们会用到线程池来做异步操作 比如下载图片.执行异步任务等.为了方便管理.继承一个线程池管理类. 在使用线程的时候只需要 submmitJob和removeJob.不会产生大量的线程.有效防止OOM 代码如下: import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent

用户和组管理类命令

1.列出当前系统上所有已经登陆的用户的用户名,注意:同一个用户登陆多次,则显示一次即可. who |cut -d' ' -f1 | sort | uniq 2.取出最后登陆到当前系统的用户的相关信息 w|tail-n1或者who|tail-n1 3.取出当前系统上被用户当做其默认shell的最多的那个shell cat /etc/passwd | awk -F':' '{print $7}'|uniq –c 结果为:/sbin/nologin awk -F':' '{a[$7]++}END{fo

转 学生管理类

看上去很工整. <!DOCTYPE html><html><head> <title>学生管理类</title></head><body><form method="post"> 学号:<input type="text" name="number"><br/> 姓名:<input type="text"